Refactor code structure for improved readability and maintainability
This commit is contained in:
237
src/effect.cpp
Normal file
237
src/effect.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user