Files
fan/src/shader.cpp
2026-09-27 01:33:35 +02:00

154 lines
3.8 KiB
C++

#include <iostream>
#include <utility>
#include <glm/gtc/type_ptr.hpp>
#include "shader.hpp"
#include "utils.hpp"
namespace Fan
{
Rc<Shader> Shader::create(const std::string& vertex_shader_path,
const std::string& fragment_shader_path)
{
auto vertex_shader = compile_shader(vertex_shader_path, GL_VERTEX_SHADER);
if (!vertex_shader) {
return nullptr;
}
auto fragment_shader = compile_shader(fragment_shader_path, GL_FRAGMENT_SHADER);
if (!fragment_shader) {
glDeleteShader(*vertex_shader);
return nullptr;
}
GLuint shader_program = glCreateProgram();
glAttachShader(shader_program, *vertex_shader);
glAttachShader(shader_program, *fragment_shader);
glLinkProgram(shader_program);
glDeleteShader(*vertex_shader);
glDeleteShader(*fragment_shader);
GLint success;
glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
if (!success) {
GLint log_length = 0;
glGetProgramiv(shader_program, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
std::string log(log_length, ' ');
glGetProgramInfoLog(shader_program, log_length, nullptr, &log[0]);
std::cerr << "ERROR: SHADER: Could not link shader program: " << log << std::endl;
}
glDeleteProgram(shader_program);
return nullptr;
}
return make_rc<Shader>(shader_program);
}
Shader::~Shader()
{
glDeleteProgram(m_id);
}
Shader::Shader(Shader&& other) noexcept
: m_id(std::exchange(other.m_id, 0))
{
}
Shader& Shader::operator=(Shader&& other) noexcept
{
if (this == &other) { return *this; }
std::swap(m_id, other.m_id);
return *this;
}
GLuint Shader::get_id() const
{
return m_id;
}
GLint Shader::get_location(const std::string &name)
{
GLint location = glGetUniformLocation(m_id, name.c_str());
if (location == -1) {
std::cerr << "WARNING: SHADER: uniform " << name << " not found" << std::endl;
}
return location;
}
void Shader::set_int(const std::string& name, GLint value)
{
glUniform1i(get_location(name), value);
}
void Shader::set_float(const std::string& name, float value)
{
glUniform1f(get_location(name), value);
}
void Shader::set_vec3(const std::string& name, float x, float y, float z)
{
glUniform3f(get_location(name), x, y, z);
}
void Shader::set_vec3(const std::string& name, const glm::vec3& value)
{
glUniform3f(get_location(name), value.x, value.y, value.z);
}
void Shader::set_mat4(const std::string& name, const glm::mat4& value)
{
GLint location = get_location(name);
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
}
Opt<GLuint> Shader::compile_shader(const std::string& source_path,
GLenum shader_type)
{
auto shader_source = read_entire_file(source_path);
if (!shader_source) {
std::cerr << "ERROR: SHADER: Could not read shader " << source_path << std::endl;
return std::nullopt;
}
GLuint shader = glCreateShader(shader_type);
const char *shader_source_str = shader_source->c_str();
glShaderSource(shader, 1, &shader_source_str, nullptr);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
GLint log_length = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
std::string log(log_length, ' ');
glGetShaderInfoLog(shader, log_length, nullptr, &log[0]);
std::cerr << "ERROR: SHADER: Could not compile shader: " << log << std::endl;
}
glDeleteShader(shader);
return std::nullopt;
}
return shader;
}
void Shader::use() const
{
if (m_id == 0) {
return;
}
glUseProgram(m_id);
}
} // namespace Fan