205 lines
5.8 KiB
C++
205 lines
5.8 KiB
C++
#include "Shader.hpp"
|
|
|
|
#include <exception>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <filesystem>
|
|
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
#include <GL/glew.h>
|
|
#include <GLFW/glfw3.h>
|
|
|
|
#include "coders/GLSLExtension.hpp"
|
|
#include "debug/Logger.hpp"
|
|
|
|
static debug::Logger logger("gl-shader");
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
GLSLExtension* Shader::preprocessor = new GLSLExtension();
|
|
Shader* Shader::used = nullptr;
|
|
|
|
Shader::Shader(uint id, Source&& vertexSource, Source&& fragmentSource)
|
|
: id(id),
|
|
vertexSource(std::move(vertexSource)),
|
|
fragmentSource(std::move(fragmentSource)) {}
|
|
|
|
Shader::~Shader() {
|
|
glDeleteProgram(id);
|
|
}
|
|
|
|
void Shader::use() {
|
|
used = this;
|
|
glUseProgram(id);
|
|
}
|
|
|
|
uint Shader::getUniformLocation(const std::string& name) {
|
|
auto found = uniformLocations.find(name);
|
|
if (found == uniformLocations.end()) {
|
|
uint location = glGetUniformLocation(id, name.c_str());
|
|
uniformLocations.try_emplace(name, location);
|
|
return location;
|
|
}
|
|
return found->second;
|
|
}
|
|
|
|
void Shader::uniformMatrix(const std::string& name, const glm::mat4& matrix) {
|
|
glUniformMatrix4fv(
|
|
getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix)
|
|
);
|
|
}
|
|
|
|
void Shader::uniformMatrix(const std::string& name, const glm::mat3& matrix) {
|
|
glUniformMatrix3fv(
|
|
getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix)
|
|
);
|
|
}
|
|
|
|
void Shader::uniform1i(const std::string& name, int x){
|
|
glUniform1i(getUniformLocation(name), x);
|
|
}
|
|
|
|
void Shader::uniform1f(const std::string& name, float x){
|
|
glUniform1f(getUniformLocation(name), x);
|
|
}
|
|
|
|
void Shader::uniform2f(const std::string& name, float x, float y){
|
|
glUniform2f(getUniformLocation(name), x, y);
|
|
}
|
|
|
|
void Shader::uniform2f(const std::string& name, const glm::vec2& xy){
|
|
glUniform2f(getUniformLocation(name), xy.x, xy.y);
|
|
}
|
|
|
|
void Shader::uniform2i(const std::string& name, const glm::ivec2& xy){
|
|
glUniform2i(getUniformLocation(name), xy.x, xy.y);
|
|
}
|
|
|
|
void Shader::uniform3f(const std::string& name, float x, float y, float z){
|
|
glUniform3f(getUniformLocation(name), x,y,z);
|
|
}
|
|
|
|
void Shader::uniform3f(const std::string& name, const glm::vec3& xyz){
|
|
glUniform3f(getUniformLocation(name), xyz.x, xyz.y, xyz.z);
|
|
}
|
|
|
|
void Shader::uniform4f(const std::string& name, const glm::vec4& xyzw) {
|
|
glUniform4f(getUniformLocation(name), xyzw.x, xyzw.y, xyzw.z, xyzw.w);
|
|
}
|
|
|
|
void Shader::uniform1v(const std::string& name, int length, const int* v) {
|
|
glUniform1iv(getUniformLocation(name), length, v);
|
|
}
|
|
|
|
void Shader::uniform1v(const std::string& name, int length, const float* v) {
|
|
glUniform1fv(getUniformLocation(name), length, v);
|
|
}
|
|
|
|
void Shader::uniform2v(const std::string& name, int length, const float* v) {
|
|
glUniform2fv(getUniformLocation(name), length, v);
|
|
}
|
|
|
|
void Shader::uniform3v(const std::string& name, int length, const float* v) {
|
|
glUniform3fv(getUniformLocation(name), length, v);
|
|
}
|
|
|
|
void Shader::uniform4v(const std::string& name, int length, const float* v) {
|
|
glUniform4fv(getUniformLocation(name), length, v);
|
|
}
|
|
|
|
static inline auto shader_deleter = [](GLuint* shader) {
|
|
glDeleteShader(*shader);
|
|
delete shader;
|
|
};
|
|
|
|
inline const uint GL_LOG_LEN = 512;
|
|
|
|
// shader should be deleted after shader program linking
|
|
using glshader = std::unique_ptr<GLuint, decltype(shader_deleter)>;
|
|
|
|
glshader compile_shader(GLenum type, const GLchar* source, const std::string& file) {
|
|
GLint success;
|
|
GLuint shader = glCreateShader(type);
|
|
glShaderSource(shader, 1, &source, nullptr);
|
|
glCompileShader(shader);
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
|
if (!success) {
|
|
GLchar infoLog[GL_LOG_LEN];
|
|
glGetShaderInfoLog(shader, GL_LOG_LEN, nullptr, infoLog);
|
|
glDeleteShader(shader);
|
|
throw std::runtime_error(
|
|
"vertex shader compilation failed (" + file + "):\n" +
|
|
std::string(infoLog)
|
|
);
|
|
}
|
|
return glshader(new GLuint(shader), shader_deleter); //-V508
|
|
}
|
|
|
|
static GLuint compile_program(
|
|
const Shader::Source& vertexSource,
|
|
const Shader::Source& fragmentSource,
|
|
const std::vector<std::string>& defines
|
|
) {
|
|
auto& preprocessor = *Shader::preprocessor;
|
|
|
|
auto vertexCode = std::move(
|
|
preprocessor
|
|
.process(vertexSource.file, vertexSource.code, false, defines)
|
|
.code
|
|
);
|
|
auto fragmentCode = std::move(
|
|
preprocessor
|
|
.process(fragmentSource.file, fragmentSource.code, false, defines)
|
|
.code
|
|
);
|
|
|
|
const GLchar* vCode = vertexCode.c_str();
|
|
const GLchar* fCode = fragmentCode.c_str();
|
|
|
|
glshader vertex =
|
|
compile_shader(GL_VERTEX_SHADER, vCode, vertexSource.file);
|
|
glshader fragment =
|
|
compile_shader(GL_FRAGMENT_SHADER, fCode, fragmentSource.file);
|
|
|
|
// Shader Program
|
|
GLint success;
|
|
GLuint program = glCreateProgram();
|
|
glAttachShader(program, *vertex);
|
|
glAttachShader(program, *fragment);
|
|
glLinkProgram(program);
|
|
|
|
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
|
|
|
if (!success) {
|
|
GLchar infoLog[GL_LOG_LEN];
|
|
glGetProgramInfoLog(program, GL_LOG_LEN, nullptr, infoLog);
|
|
throw std::runtime_error(
|
|
"shader program linking failed:\n" + std::string(infoLog)
|
|
);
|
|
}
|
|
return program;
|
|
}
|
|
|
|
void Shader::recompile(const std::vector<std::string>& defines) {
|
|
GLuint newProgram = compile_program(vertexSource, fragmentSource, defines);
|
|
glDeleteProgram(id);
|
|
id = newProgram;
|
|
uniformLocations.clear();
|
|
logger.info() << "shader " << id << " has been recompiled";
|
|
}
|
|
|
|
std::unique_ptr<Shader> Shader::create(
|
|
Source&& vertexSource, Source&& fragmentSource
|
|
) {
|
|
return std::make_unique<Shader>(
|
|
compile_program(vertexSource, fragmentSource, {}),
|
|
std::move(vertexSource),
|
|
std::move(fragmentSource)
|
|
);
|
|
}
|
|
|
|
Shader& Shader::getUsed() {
|
|
return *used;
|
|
}
|