/* * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include #include #include "tilesheetpixels.hpp" namespace nostalgia::core { void TileSheetPixels::setPixelSizeMod(float sm) noexcept { m_pixelSizeMod = sm; } ox::Error TileSheetPixels::buildShader() noexcept { const auto Vshad = ox::sfmt(VShad, glutils::GlslVersion); const auto Fshad = ox::sfmt(FShad, glutils::GlslVersion); return glutils::buildShaderProgram(Vshad, Fshad).moveTo(&m_shader); } void TileSheetPixels::draw(bool update, const geo::Vec2 &scroll) noexcept { glUseProgram(m_shader); glBindVertexArray(m_bufferSet.vao); if (update) { glutils::sendVbo(m_bufferSet); } const auto uniformScroll = glGetUniformLocation(m_shader, "vScroll"); glUniform2f(uniformScroll, scroll.x, scroll.y); glDrawElements(GL_TRIANGLES, static_cast(m_bufferSet.elements.size()), GL_UNSIGNED_INT, nullptr); } void TileSheetPixels::initBufferSet(const geo::Vec2 &paneSize, const TileSheet &img, const Palette &pal) noexcept { // vao m_bufferSet.vao = glutils::generateVertexArrayObject(); glBindVertexArray(m_bufferSet.vao); // vbo & ebo m_bufferSet.vbo = glutils::generateBuffer(); m_bufferSet.ebo = glutils::generateBuffer(); setBufferObjects(paneSize, img, pal, &m_bufferSet); glutils::sendVbo(m_bufferSet); glutils::sendEbo(m_bufferSet); // vbo layout const auto posAttr = static_cast(glGetAttribLocation(m_shader, "vPosition")); glEnableVertexAttribArray(posAttr); glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float), nullptr); const auto colorAttr = static_cast(glGetAttribLocation(m_shader, "vColor")); glEnableVertexAttribArray(colorAttr); glVertexAttribPointer(colorAttr, 3, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float), reinterpret_cast(2 * sizeof(float))); } geo::Vec2 TileSheetPixels::pixelSize(const geo::Vec2 &paneSize) const noexcept { const auto [sw, sh] = paneSize; constexpr float ymod = 0.35f / 10.0f; const auto xmod = ymod * sh / sw; return {xmod * m_pixelSizeMod, ymod * m_pixelSizeMod}; } void TileSheetPixels::setPixelBufferObject(const geo::Vec2 &paneSize, unsigned vertexRow, float x, float y, Color16 color, float *vbo, GLuint *ebo) const noexcept { const auto [xmod, ymod] = pixelSize(paneSize); x *= xmod; y *= -ymod; x -= 1.0f; y += 1.0f - ymod; const auto r = redf(color), g = greenf(color), b = bluef(color); // don't worry, these memcpys gets optimized to something much more ideal const float vertices[VertexVboLength] = { x, y, r, g, b, // bottom left x + xmod, y, r, g, b, // bottom right x + xmod, y + ymod, r, g, b, // top right x, y + ymod, r, g, b, // top left }; memcpy(vbo, vertices, sizeof(vertices)); const GLuint elms[VertexEboLength] = { vertexRow + 0, vertexRow + 1, vertexRow + 2, vertexRow + 2, vertexRow + 3, vertexRow + 0, }; memcpy(ebo, elms, sizeof(elms)); } void TileSheetPixels::setBufferObjects(const geo::Vec2 &paneSize, const TileSheet &img, const Palette &pal, glutils::BufferSet *bg) noexcept { const auto setPixel = [this, paneSize, bg, img, pal](std::size_t i, uint8_t p) { const auto color = pal.colors[p]; const auto pt = idxToPt(static_cast(i), img.columns); const auto fx = static_cast(pt.x); const auto fy = static_cast(pt.y); const auto vbo = &bg->vertices[i * VertexVboLength]; const auto ebo = &bg->elements[i * VertexEboLength]; setPixelBufferObject(paneSize, i * VertexVboRows, fx, fy, color, vbo, ebo); }; // set buffer lengths const auto width = img.columns * TileWidth; const auto height = img.rows * TileHeight; const auto tiles = static_cast(width * height); m_bufferSet.vertices.resize(tiles * VertexVboLength); m_bufferSet.elements.resize(tiles * VertexEboLength); // set pixels if (img.bpp == 4) { for (std::size_t i = 0; i < img.pixels.size(); ++i) { const auto colorIdx1 = img.pixels[i] & 0xF; const auto colorIdx2 = img.pixels[i] >> 4; setPixel(i * 2 + 0, colorIdx1); setPixel(i * 2 + 1, colorIdx2); } } else { for (std::size_t i = 0; i < img.pixels.size(); ++i) { const auto p = img.pixels[i]; setPixel(i, p); } } } }