RyanHub – file viewer
filename: src/renderer.cpp
branch: main
back to repo
#include "renderer.h"
#include "constants.h"
#include <algorithm>

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>

Renderer::Renderer(Shader& shaderProgram, GLuint* textureIDs, int textureCount): 
	shaderProgram(shaderProgram),
	textureIDs(textureIDs),
	textureCount(textureCount) 
{
	init();
}
Renderer::~Renderer() {
    glDeleteVertexArrays(1, &masterVAO);
    glDeleteBuffers(1, &masterVBO);
    glDeleteBuffers(1, &masterEBO);

    delete[] textureUniformLocations;
}

void Renderer::init() {
    glGenVertexArrays(1, &masterVAO);
    glGenBuffers(1, &masterVBO);
    glGenBuffers(1, &masterEBO);

    glBindVertexArray(masterVAO);

    glBindBuffer(GL_ARRAY_BUFFER, masterVBO);
    glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_BUFFER_SIZE, nullptr, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, masterEBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_INDEX_BUFFER_SIZE, nullptr, GL_DYNAMIC_DRAW);

    GLsizei stride = 9 * sizeof(GLfloat);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, (void*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, stride, (void*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, stride, (void*)(8 * sizeof(GLfloat)));
    glEnableVertexAttribArray(3);

    glBindVertexArray(0);

    textureUniformLocations = new GLint[textureCount];
    for (int i = 0; i < textureCount; ++i) {
        std::string samplerName = "textures[" + std::to_string(i) + "]";
        textureUniformLocations[i] = glGetUniformLocation(shaderProgram.ID, samplerName.c_str());
    }
}
void Renderer::renderFrame() {
    glBindVertexArray(masterVAO);
    //std::cout << "masterVertices size: " << masterVertices.size() << std::endl;


    for (int i = 0; i < textureCount; ++i) {
        glActiveTexture(GL_TEXTURE0 + i);
        glBindTexture(GL_TEXTURE_2D, textureIDs[i]);
    }
    shaderProgram.Activate();
    for (int i = 0; i < textureCount; ++i) {
        glUniform1i(textureUniformLocations[i], i);
    }
    glBindBuffer(GL_ARRAY_BUFFER, masterVBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, masterVertices.size() * sizeof(GLfloat), masterVertices.data());

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, masterEBO);
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, masterIndices.size() * sizeof(GLuint), masterIndices.data());

    glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(masterIndices.size()), GL_UNSIGNED_INT, 0);

    glBindVertexArray(0);

    masterVertices.clear();
    masterIndices.clear();
}
void Renderer::drawQuad(const QuadVertices& verts, const ColorRGB& color, int textureIndex, const float* uvs) {
    GLfloat quadVertices[36];
    const float defaultUVs[8] = { 0,1, 0,0, 1,0, 1,1 };
    const float* uv = uvs ? uvs : defaultUVs;

    const GLfloat positions[8] = { verts.x3, verts.y3, verts.x1, verts.y1, verts.x2, verts.y2, verts.x4, verts.y4 };
    for (int i = 0; i < 4; ++i) {
        quadVertices[i * 9 + 0] = positions[i * 2 + 0];
        quadVertices[i * 9 + 1] = positions[i * 2 + 1];
        quadVertices[i * 9 + 2] = 0.0f;
        quadVertices[i * 9 + 3] = color.r;
        quadVertices[i * 9 + 4] = color.g;
        quadVertices[i * 9 + 5] = color.b;
        quadVertices[i * 9 + 6] = uv[i * 2 + 0];
        quadVertices[i * 9 + 7] = uv[i * 2 + 1];
        quadVertices[i * 9 + 8] = static_cast<float>(textureIndex);
    }
    GLuint quadIndices[] = { 0,1,2,2,3,0 };
    size_t baseIndex = masterVertices.size() / 9;
    masterVertices.insert(masterVertices.end(), quadVertices, quadVertices + 36);
    for (auto index : quadIndices) {
        masterIndices.push_back(static_cast<GLuint>(baseIndex + index));
    }
}

GLuint loadTexture(const char* filepath) {
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);

    // Set filtering and wrapping
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    // Load image data
    int width, height, nrChannels;
    unsigned char* data = stbi_load(filepath, &width, &height, &nrChannels, 0);
    if (data) {
        GLenum format;
        if (nrChannels == 1) {
            format = GL_RED; // Grayscale / font textures
        }
        else if (nrChannels == 3) {
            format = GL_RGB; // Standard color
        }
        else if (nrChannels == 4) {
            format = GL_RGBA; // Color + alpha
        }
        else {
            std::cerr << "Warning: Unhandled channel count (" << nrChannels << "), defaulting to RGB" << std::endl;
            format = GL_RGB;
        }

        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);

        std::cout << "Loaded texture: " << filepath
            << " (" << width << "x" << height << ", channels: " << nrChannels << ")" << std::endl;
    }
    else {
        std::cerr << "Failed to load texture: " << filepath << std::endl;
    }

    stbi_image_free(data);
    return textureID;
}
void captureRegion(int x, int y, int w, int h, const std::string& filepath) {

    std::vector<unsigned char> pixels(w * h * 4);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glReadBuffer(GL_BACK);
    glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());

    int rowBytes = w * 4;
    for (int row = 0; row < h / 2; ++row) {
        unsigned char* top = pixels.data() + row * rowBytes;
        unsigned char* bottom = pixels.data() + (h - 1 - row) * rowBytes;
        for (int col = 0; col < rowBytes; ++col) {
            std::swap(top[col], bottom[col]);
        }
    }

    if (!stbi_write_png(filepath.c_str(), w, h, 4, pixels.data(), rowBytes))
        std::cerr << "Failed to write " << filepath << "\n";
    else
        std::cout << "Saved " << filepath << "\n";
}