Refactor code structure for improved readability and maintainability

This commit is contained in:
Diego Lopes
2026-03-18 00:25:31 -04:00
commit e538df3673
34 changed files with 9243 additions and 0 deletions

237
src/effect.cpp Normal file
View File

@@ -0,0 +1,237 @@
#include "effect.h"
#include <vector>
#include <regex>
#include <format>
#include <iostream>
#include <stdexcept>
#include "globals.h"
#include "texture.h"
GLuint Effect::g_dummyVAO = 0;
static std::vector<std::string> SplitLines(const std::string& str) {
std::vector<std::string> lines;
size_t start = 0, end = 0;
while ((end = str.find_first_of("\r\n", start)) != std::string::npos) {
lines.push_back(str.substr(start, end - start));
start = end + 1;
}
if (start < str.size()) {
lines.push_back(str.substr(start));
}
return lines;
}
Effect::Effect(const std::string& fragmentShaderSource)
{
const std::string vertexShaderSource =
#include "fx_vertex.glsl.h"
;
std::string code =
"#version 460 core\n\n"
"out vec4 FragColor;\n\n"
"uniform float uTime;\n"
"uniform vec4 uMouse;\n"
"uniform vec2 uDesktopSize;\n";
for (size_t i = 0; i < g_Displays.size(); i++) {
code += "uniform vec4 uDisplay" + std::to_string(i) + ";\n";
}
// reset line number
code += "#line 1\n";
GLuint vertexShader = CreateShader(vertexShaderSource, GL_VERTEX_SHADER);
if (!vertexShader) {
throw std::runtime_error("Failed to compile vertex shader");
return;
}
GLuint fragmentShader = CreateShader(code + fragmentShaderSource, GL_FRAGMENT_SHADER);
if (!fragmentShader) {
glDeleteShader(vertexShader);
throw std::runtime_error("Failed to compile fragment shader");
return;
}
m_program = glCreateProgram();
glAttachShader(m_program, vertexShader);
glAttachShader(m_program, fragmentShader);
glLinkProgram(m_program);
GLint success;
glGetProgramiv(m_program, GL_LINK_STATUS, &success);
if (!success) {
GLint logLength;
glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &logLength);
std::string log(logLength, '\0');
glGetProgramInfoLog(m_program, logLength, nullptr, log.data());
throw std::runtime_error("Failed to link shader program: " + log);
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// Create a dummy VAO to allow rendering without a real VAO bound
if (!g_dummyVAO) {
glGenVertexArrays(1, &g_dummyVAO);
}
}
Effect::~Effect()
{
if (m_program) {
glDeleteProgram(m_program);
m_program = 0;
}
}
void Effect::Use() const
{
glUseProgram(m_program);
}
GLuint Effect::GetUniformLocation(const std::string& name)
{
auto it = m_uniformLocations.find(name);
if (it != m_uniformLocations.end()) {
return it->second;
}
GLuint location = glGetUniformLocation(m_program, name.c_str());
m_uniformLocations[name] = location;
return location;
}
void Effect::SetSampler(const std::string &name, GLuint textureUnit)
{
glUniform1i(GetUniformLocation(name), textureUnit);
}
void Effect::SetTexture(const std::string &name, const Texture &texture, size_t textureUnit)
{
texture.Bind();
glActiveTexture(GL_TEXTURE0 + static_cast<GLuint>(textureUnit));
SetSampler(name, static_cast<GLuint>(textureUnit));
}
void Effect::SetFloat(const std::string &name, float value)
{
glUniform1f(GetUniformLocation(name), value);
}
void Effect::SetVector2(const std::string &name, const Vector2 &value)
{
glUniform2f(GetUniformLocation(name), value.x, value.y);
}
void Effect::SetVector3(const std::string &name, const Vector3 &value)
{
glUniform3f(GetUniformLocation(name), value.x, value.y, value.z);
}
void Effect::SetVector4(const std::string &name, const Vector4 &value)
{
glUniform4f(GetUniformLocation(name), value.x, value.y, value.z, value.w);
}
void Effect::SetMatrix4(const std::string &name, const Matrix4 &value)
{
glUniformMatrix4fv(GetUniformLocation(name), 1, GL_FALSE, value.data());
}
void Effect::Render()
{
Use();
// Set default uniforms
SetFloat("uTime", g_Time);
SetVector4("uMouse", g_Mouse);
SetVector2("uDesktopSize", g_DesktopSize);
for (size_t i = 0; i < g_Displays.size(); i++) {
SetVector4("uDisplay" + std::to_string(i), g_Displays[i]);
}
glBindVertexArray(g_dummyVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
GLuint Effect::CreateShader(const std::string &source, GLenum shaderType)
{
GLuint shader = glCreateShader(shaderType);
const char* src = source.c_str();
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
std::vector<std::string> sourceLines = SplitLines(source);
GLint logLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
std::string log(logLength, '\0');
glGetShaderInfoLog(shader, logLength, nullptr, log.data());
std::string logText = "";
std::string shaderTypeText = "??";
switch (shaderType) {
case GL_VERTEX_SHADER: shaderTypeText = "Vertex Shader"; break;
case GL_FRAGMENT_SHADER: shaderTypeText = "Fragment Shader"; break;
default: shaderTypeText = "Shader"; break;
}
std::smatch match;
int lineNumber = 0;
int column = -1;
std::string errorMessage;
// Mesa/Intel: "0:15(10): error: msg" or "ERROR: 0:15: msg"
std::regex mesaRegex(R"((?:ERROR: )?\d+:(\d+)(?:\((\d+)\))?:\s*(.*))");
// NVIDIA: "0(11) : error C7530: msg"
std::regex nvidiaRegex(R"(\d+\((\d+)\)\s*:\s*(.*))");
if (std::regex_search(log, match, mesaRegex)) {
lineNumber = std::stoi(match[1].str());
column = match[2].matched ? std::stoi(match[2].str()) : -1;
errorMessage = match[3].str();
} else if (std::regex_search(log, match, nvidiaRegex)) {
lineNumber = std::stoi(match[1].str());
errorMessage = match[2].str();
}
if (lineNumber > 0) {
logText = std::format("{} error at line {}: {}\nOffending code:\n", shaderTypeText, lineNumber, errorMessage);
if (lineNumber <= static_cast<int>(sourceLines.size())) {
std::string line = sourceLines[lineNumber - 1];
// add a ^ at column in a new line
std::string arrow(line.size(), ' ');
if (column >= 0 && column < static_cast<int>(arrow.size())) {
arrow[column] = '^';
}
if (lineNumber - 2 >= 0) {
logText += std::format("{}\n", sourceLines[lineNumber - 2]);
}
logText += std::format("{}\n{}", line, arrow);
if (lineNumber < static_cast<int>(sourceLines.size())) {
logText += std::format("\n{}", sourceLines[lineNumber]);
}
}
}
std::cout << logText << std::endl;
glDeleteShader(shader);
return 0;
}
return shader;
}