diff --git a/src/nostalgia/core/sdl/gfx.cpp b/src/nostalgia/core/sdl/gfx.cpp index 8a10f7aa..399cb329 100644 --- a/src/nostalgia/core/sdl/gfx.cpp +++ b/src/nostalgia/core/sdl/gfx.cpp @@ -111,13 +111,10 @@ ox::Error loadBgTileSheet(Context *ctx, palettePath = tilesheet.defaultPalette; } oxReturnError(readObj(ctx, palettePath).get(&palette)); - const unsigned bytesPerTile = tilesheet.bpp == 8 ? 64 : 32; const auto tiles = tilesheet.pixels.size() / bytesPerTile; const int width = 8; const int height = 8 * tiles; - //const auto format = SDL_PIXELFORMAT_INDEX8; - //const auto sdlPalette = createSDL_Palette(palette); std::vector pixels; if (bytesPerTile == 64) { // 8 BPP pixels.resize(tilesheet.pixels.size()); @@ -131,20 +128,7 @@ ox::Error loadBgTileSheet(Context *ctx, pixels[i * 2 + 1] = toColor32(palette.colors[tilesheet.pixels[i] >> 4]); } } - - oxReturnError(renderer::loadTexture(ctx, section, pixels.data(), width, height)); - - //auto texture = SDL_CreateTextureFromSurface(nullptr, surface); - //SDL_FreeSurface(surface); - //SDL_FreePalette(sdlPalette); - - //auto sectionIdx = static_cast(section); - //if (id->bgTextures[sectionIdx]) { - // SDL_DestroyTexture(id->bgTextures[sectionIdx]); - //} - //id->bgTextures[sectionIdx] = texture; - - return OxError(0); + return renderer::loadBgTexture(ctx, section, pixels.data(), width, height); } ox::Error loadSpriteTileSheet(Context*, @@ -156,8 +140,8 @@ ox::Error loadSpriteTileSheet(Context*, void drawBackground(Context*, const TileMap &tm, SDL_Texture *tex) { if (tex) { + oxTrace("nostalgia::core::sdl::drawBackground", "Drawing background"); constexpr auto DstSize = 8 * Scale; - oxTracef("nostalgia::core::sdl::drawBackground", "Drawing background"); SDL_Rect src = {}, dst = {}; src.x = 0; src.w = 8; diff --git a/src/nostalgia/core/userland/CMakeLists.txt b/src/nostalgia/core/userland/CMakeLists.txt index 5a92b6ca..cbb0061b 100644 --- a/src/nostalgia/core/userland/CMakeLists.txt +++ b/src/nostalgia/core/userland/CMakeLists.txt @@ -1,6 +1,7 @@ add_library( NostalgiaCore-Userspace OBJECT gfx_opengl.cpp + glutils.cpp media.cpp ) @@ -19,7 +20,6 @@ target_link_libraries( OxFS OxStd NostalgiaCore - #GLESv2 ${OPENGL_gl_LIBRARY} ) diff --git a/src/nostalgia/core/userland/gfx.hpp b/src/nostalgia/core/userland/gfx.hpp index 65b44bb3..51589414 100644 --- a/src/nostalgia/core/userland/gfx.hpp +++ b/src/nostalgia/core/userland/gfx.hpp @@ -18,6 +18,6 @@ ox::Error init(Context *ctx); ox::Error shutdown(Context *ctx); -ox::Error loadTexture(Context *ctx, int section, void *bytes, int w, int h); +ox::Error loadBgTexture(Context *ctx, int section, void *bytes, int w, int h); } diff --git a/src/nostalgia/core/userland/gfx_opengl.cpp b/src/nostalgia/core/userland/gfx_opengl.cpp index 42b4cd77..d619b0fa 100644 --- a/src/nostalgia/core/userland/gfx_opengl.cpp +++ b/src/nostalgia/core/userland/gfx_opengl.cpp @@ -6,58 +6,154 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include -#define GL_SILENCE_DEPRECATION - #include +#include #include -#include - -#define GL_GLEXT_PROTOTYPES 1 -//#include -#ifdef OX_OS_Darwin -#include -#else -#include -#endif +#include #include +#include "glutils.hpp" + namespace nostalgia::core { using TileMap = std::array, 128>; -struct GlImplData { - std::array bgTileMaps{}; - std::array bgTextures{}; - int64_t prevFpsCheckTime = 0; - uint64_t draws = 0; +namespace renderer { + +constexpr auto TileRows = 16; +constexpr auto TileColumns = 16; +constexpr auto TileCount = TileRows * TileColumns; +constexpr auto BgVertexVboRowLength = 6; +constexpr auto BgVertexVboLength = 16; +constexpr auto BgVertexEboLength = 6; + +struct BackgroundBufferset: public Bufferset { + std::array bgVertices; + std::array bgEbos; }; +struct GlImplData { + std::array bgTileMaps{}; + int64_t prevFpsCheckTime = 0; + uint64_t draws = 0; + std::array backgrounds; + GLuint bgShader = 0; +}; -namespace renderer { +constexpr const GLchar *bgvshad = R"( + #version 150 + in vec2 vTexCoord; + in vec2 position; + out vec2 fTexCoord; + void main() { + gl_Position = vec4(position, 0.0, 1.0); + fTexCoord = vTexCoord; + })"; + +constexpr const GLchar *bgfshad = R"( + #version 150 + out vec4 outColor; + in vec2 fTexCoord; + uniform sampler2D image; + void main() { + //outColor = vec4(0.0, 0.7, 1.0, 1.0); + outColor = texture(image, fTexCoord) * vec4(1.0, 1.0, 1.0, 1.0); + })"; + +void initTileBufferObjects(unsigned vi, float x, float y, float *vbo, GLuint *ebo) { + // don't worry, this gets optimized to something much more ideal + constexpr float xmod = 0.06f; + x *= xmod; + y *= 0.1f; + const float vertices[BgVertexVboLength] = { + x, y, 0, 0.04, // bottom left + x + xmod, y, 1, 0.04, // bottom right + x + xmod, y + 0.1f, 1, 0, // top right + x, y + 0.1f, 0, 0, // top left + }; + memcpy(vbo, vertices, sizeof(vertices)); + const GLuint elms[BgVertexEboLength] = { + vi + 0, vi + 1, vi + 2, + vi + 2, vi + 3, vi + 0, + }; + memcpy(ebo, elms, sizeof(elms)); +} + +void initBackgroundBufferObjects(GLuint shader, BackgroundBufferset *bs) { + auto i = 0u; + for (auto x = 0u; x < TileColumns; ++x) { + for (auto y = 0u; y < TileRows; ++y) { + const auto vi = i * BgVertexVboLength; + auto vbo = &bs->bgVertices[vi]; + auto ebo = &bs->bgEbos[i * BgVertexEboLength]; + initTileBufferObjects(vi, x, y, vbo, ebo); + ++i; + } + } + // vbo + glBindBuffer(GL_ARRAY_BUFFER, bs->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(bs->bgVertices), &bs->bgVertices, GL_DYNAMIC_DRAW); + // ebo + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bs->ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(bs->bgEbos), &bs->bgEbos, GL_STATIC_DRAW); + // vbo layout + auto posAttr = static_cast(glGetAttribLocation(shader, "position")); + glEnableVertexAttribArray(posAttr); + glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); + auto texCoordAttr = static_cast(glGetAttribLocation(shader, "vTexCoord")); + glEnableVertexAttribArray(texCoordAttr); + glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), + ox::bit_cast((2 * sizeof(float)))); +} + +void initBackgroundBufferset(GLuint shader, BackgroundBufferset *bufferset) { + // vao + glGenVertexArrays(1, &bufferset->vao); + glBindVertexArray(bufferset->vao); + // vbo & ebo + glGenBuffers(1, &bufferset->vbo); + glGenBuffers(1, &bufferset->ebo); + initBackgroundBufferObjects(shader, bufferset); + + //glActiveTexture(GL_TEXTURE0); + //glBindTexture(GL_TEXTURE_2D, texture); + //auto texAttr = static_cast(glGetUniformLocation(shaderPrgm, "image")); + //glUniform1i(static_cast(texAttr), static_cast(texture)); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} ox::Error init(Context *ctx) { const auto id = new GlImplData; ctx->setRendererData(id); + id->bgShader = buildShaderProgram(bgvshad, bgfshad); + for (auto &bg : id->backgrounds) { + initBackgroundBufferset(id->bgShader, &bg); + } return OxError(0); } ox::Error shutdown(Context *ctx) { const auto id = ctx->rendererData(); + for (auto &bg : id->backgrounds) { + destroy(bg); + } ctx->setRendererData(nullptr); delete id; return OxError(0); } -ox::Error loadTexture(Context *ctx, int section, void *pixels, int w, int h) { - oxTracef("nostalgia::core::gfx::gl", "loadTexture: { section: {}, w: {}, h: {} }", section, w, h); - const auto &id = ctx->rendererData(); - auto &texId = id->bgTextures[static_cast(section)]; - glGenTextures(1, &texId); +ox::Error loadTexture(GLuint *texId, void *pixels, int w, int h) { + if (*texId == 0) { + glGenTextures(1, texId); + } glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texId); + glBindTexture(GL_TEXTURE_2D, *texId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -66,11 +162,18 @@ ox::Error loadTexture(Context *ctx, int section, void *pixels, int w, int h) { return OxError(0); } +ox::Error loadBgTexture(Context *ctx, int section, void *pixels, int w, int h) { + oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { section: {}, w: {}, h: {} }", section, w, h); + const auto &id = ctx->rendererData(); + const auto texId = &id->backgrounds[static_cast(section)].tex; + return loadTexture(texId, pixels, w, h); +} + } void draw(Context *ctx) { - const auto id = ctx->rendererData(); + const auto id = ctx->rendererData(); ++id->draws; if (id->draws >= 5000) { using namespace std::chrono; @@ -86,23 +189,16 @@ void draw(Context *ctx) { } glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); - auto &texId = id->bgTextures[0]; - glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glBindTexture(GL_TEXTURE_2D, texId); + //glEnable(GL_TEXTURE_2D); + //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - // Draw a textured quad - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(-0.01, 1); - glTexCoord2f(0, 1); glVertex2f(-0.01, -1); - glTexCoord2f(1, 1); glVertex2f( 0.01, -1); - glTexCoord2f(1, 0); glVertex2f( 0.01, 1); - glEnd(); - - glDisable(GL_TEXTURE_2D); - - for (std::size_t i = 0; i < id->bgTileMaps.size(); i++) { + for (std::size_t i = 0; i < id->backgrounds.size(); i++) { + const auto &bg = id->backgrounds[i]; + glUseProgram(id->bgShader); + glBindTexture(GL_TEXTURE_2D, bg.tex); + glBindVertexArray(bg.vao); + glDrawElements(GL_TRIANGLES, bg.bgEbos.size(), GL_UNSIGNED_INT, 0); } } @@ -129,7 +225,7 @@ void setSprite(Context*, } void setTile(Context *ctx, int layer, int column, int row, uint8_t tile) { - const auto id = ctx->rendererData(); + const auto id = ctx->rendererData(); const auto z = static_cast(layer); const auto y = static_cast(row); const auto x = static_cast(column); diff --git a/src/nostalgia/core/userland/glutils.cpp b/src/nostalgia/core/userland/glutils.cpp new file mode 100644 index 00000000..dab80f8a --- /dev/null +++ b/src/nostalgia/core/userland/glutils.cpp @@ -0,0 +1,68 @@ +/* + * Copyright 2016 - 2021 gary@drinkingtea.net + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include "glutils.hpp" + +namespace nostalgia::core::renderer { + +[[nodiscard]] GLuint buildShader(GLuint shaderType, const GLchar *src, const char *shaderName) { + auto shader = glCreateShader(shaderType); + glShaderSource(shader, 1, &src, nullptr); + glCompileShader(shader); + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + static const auto errMsgSize = 1000; + char errMsg[errMsgSize]; + glGetShaderInfoLog(shader, errMsgSize, nullptr, errMsg); + oxTracef("nostalgia::core::gfx::gl", "shader compile error in {}: {}", shaderName, errMsg); + glDeleteShader(shader); + shader = 0; + } + return shader; +} + +[[nodiscard]] GLuint buildVertShader(const GLchar *src, const char *shaderName) { + return buildShader(GL_VERTEX_SHADER, src, shaderName); +} + +[[nodiscard]] GLuint buildFragShader(const GLchar *src, const char *shaderName) { + return buildShader(GL_FRAGMENT_SHADER, src, shaderName); +} + +[[nodiscard]] GLuint buildShaderProgram(const GLchar *vert, const GLchar *frag) { + GLuint prgm = 0; + const auto vs = buildVertShader(vert, "vshad"); + if (!vs) { + glDeleteShader(vs); + } else { + const auto fs = buildFragShader(frag, "fshad"); + if (!fs) { + // cleanup shaders that were created + glDeleteShader(fs); + } else { + prgm = glCreateProgram(); + if (prgm) { + glAttachShader(prgm, vs); + glAttachShader(prgm, fs); + glLinkProgram(prgm); + } + } + } + return prgm; +} + +void destroy(const Bufferset &bufferset) { + glDeleteVertexArrays(1, &bufferset.vao); + glDeleteBuffers(1, &bufferset.ebo); + glDeleteBuffers(1, &bufferset.vbo); +} + +} diff --git a/src/nostalgia/core/userland/glutils.hpp b/src/nostalgia/core/userland/glutils.hpp new file mode 100644 index 00000000..d5632103 --- /dev/null +++ b/src/nostalgia/core/userland/glutils.hpp @@ -0,0 +1,31 @@ +/* + * Copyright 2016 - 2021 gary@drinkingtea.net + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#define GL_GLEXT_PROTOTYPES 1 +#ifdef OX_OS_Darwin +#define GL_SILENCE_DEPRECATION +#include +#else +#include +#endif + +namespace nostalgia::core::renderer { + +struct Bufferset { + GLuint vao = 0; + GLuint vbo = 0; + GLuint ebo = 0; + GLuint tex = 0; + GLsizei eboElements = 0; +}; + +[[nodiscard]] GLuint buildShaderProgram(const GLchar *vert, const GLchar *frag); + +void destroy(const Bufferset &bufferset); + +}