terrain-gen/src/shader.cpp

173 lines
4.3 KiB
C++

#include "shader.h"
Shader::Shader(std::string name, bool useGeom)
{
initializeOpenGLFunctions();
m_programId = glCreateProgram();
loadShader(Config::shaderPath() + name + ".vert", GL_VERTEX_SHADER);
loadShader(Config::shaderPath() + name + ".frag", GL_FRAGMENT_SHADER);
if (useGeom) {
loadShader(Config::shaderPath() + name + ".geom", GL_GEOMETRY_SHADER);
}
linkProgram();
}
Shader::~Shader()
{
glDeleteProgram(m_programId);
}
void Shader::loadShader(std::string name, GLenum shaderType)
{
GLuint shader = glCreateShader(shaderType);
std::string shaderSource = loadSource(name);
const char* shaderSourcePointer = shaderSource.c_str();
glShaderSource(shader, 1, &shaderSourcePointer, nullptr);
glCompileShader(shader);
if (checkCompilationStatus(shader)) {
m_shaders.insert(shader);
} else {
std::cerr
<< "Shader compilation failed" << std::endl
<< "File: " << name << std::endl
<< "*** Errors ***" << std::endl;
logCompilationError(shader);
std::cerr
<< "** Source code ***" << std::endl
<< shaderSource << std::endl;
glDeleteShader(shader);
}
}
std::string Shader::loadSource(std::string path)
{
std::ifstream shaderSourceFile(path);
std::string line;
std::stringstream source;
QRegularExpression re("^\\s*#\\s*include\\s+\"(.+)\"");
if (shaderSourceFile.is_open()) {
while (getline(shaderSourceFile, line)) {
QRegularExpressionMatch match = re.match(QString::fromStdString(line));
if (match.hasMatch()) {
QString includedFile = match.captured(1);
source << Shader::loadSource(Config::shaderPath() + includedFile.toStdString());
} else {
source << line << std::endl;
}
}
} else {
std::cerr << "Error while opening file " << path << std::endl;
}
return source.str();
}
bool Shader::checkCompilationStatus(GLuint shader)
{
GLint result;
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
return result == GL_TRUE;
}
void Shader::logCompilationError(GLuint shader)
{
GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
char* infoLog = new char[unsigned(maxLength)];
glGetShaderInfoLog(shader, maxLength, &maxLength, infoLog);
std::cerr << infoLog << std::endl;
delete[] infoLog;
}
void Shader::linkProgram()
{
for (GLuint shader: m_shaders) {
glAttachShader(m_programId, shader);
}
glLinkProgram(m_programId);
if (!checkLinkStatus()) {
std::cerr << "Linking program failed" << std::endl;
logLinkError();
glDeleteProgram(m_programId);
}
for (GLuint shader: m_shaders) {
glDetachShader(m_programId, shader);
glDeleteShader(shader);
}
m_shaders.clear();
}
bool Shader::checkLinkStatus()
{
GLint result;
glGetProgramiv(m_programId, GL_LINK_STATUS, &result);
return result == GL_TRUE;
}
void Shader::logLinkError()
{
GLint maxLength = 0;
glGetProgramiv(m_programId, GL_INFO_LOG_LENGTH, &maxLength);
char* infoLog = new char[unsigned(maxLength)];
glGetProgramInfoLog(m_programId, maxLength, &maxLength, infoLog);
std::cerr << infoLog << std::endl;
delete[] infoLog;
}
void Shader::setTexture(std::string location, GLuint slot, GLuint textureId, GLenum target)
{
bind();
glUniform1i(uniformLocation(location), int(slot - GL_TEXTURE0));
glActiveTexture(slot);
glBindTexture(target, textureId);
release();
}
void Shader::bind()
{
glUseProgram(m_programId);
}
void Shader::release()
{
glUseProgram(0);
}
GLint Shader::uniformLocation(std::string location)
{
try {
return m_uniformLocationMap.at(location);
} catch (const std::out_of_range&) {
m_uniformLocationMap[location] = glGetUniformLocation(m_programId, location.c_str());
return m_uniformLocationMap[location];
}
}
GLint Shader::attributeLocation(std::string location)
{
try {
return m_attributeLocationMap.at(location);
} catch (const std::out_of_range&) {
m_attributeLocationMap[location] = glGetAttribLocation(m_programId, location.c_str());
return m_attributeLocationMap[location];
}
}