/* * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include #include #include "tilesheeteditormodel.hpp" #include "tilesheetpixels.hpp" namespace nostalgia::core { TileSheetPixels::TileSheetPixels(TileSheetEditorModel *model) noexcept: m_model(model) { } 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) 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); 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) noexcept { // set buffer lengths const auto subSheet = m_model->activeSubSheet(); const auto &pal = m_model->pal(); const auto width = subSheet->columns * TileWidth; const auto height = subSheet->rows * TileHeight; const auto pixels = static_cast(width * height); m_bufferSet.vertices.resize(pixels * VertexVboLength); m_bufferSet.elements.resize(pixels * VertexEboLength); // set pixels subSheet->walkPixels(m_model->img().bpp, [&](std::size_t i, uint8_t p) { auto color = pal.colors[p]; const auto pt = idxToPt(static_cast(i), subSheet->columns); const auto fx = static_cast(pt.x); const auto fy = static_cast(pt.y); const auto vbo = &m_bufferSet.vertices[i * VertexVboLength]; const auto ebo = &m_bufferSet.elements[i * VertexEboLength]; if (m_model->pixelSelected(i)) { const auto r = red16(color) / 2; const auto g = (green16(color) + 20) / 2; const auto b = (blue16(color) + 31) / 2; color = color16(r, g, b); } setPixelBufferObject(paneSize, i * VertexVboRows, fx, fy, color, vbo, ebo); }); } }