206 lines
6.7 KiB
C++
206 lines
6.7 KiB
C++
#include "shader.hpp"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <print>
|
|
#include <filesystem>
|
|
#include "dbc.hpp"
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
inline std::string read_file(const std::string& filename) {
|
|
// load the file
|
|
std::ifstream in_file{filename, std::ios::binary};
|
|
|
|
// get the size of the file
|
|
std::stringstream in_str;
|
|
in_str << in_file.rdbuf();
|
|
return in_str.str();
|
|
}
|
|
|
|
void check_error(const std::string& what, unsigned int thing, GLenum check_type) {
|
|
int success = 0;
|
|
char infoLog[512] = {0};
|
|
|
|
if(check_type == GL_LINK_STATUS) {
|
|
glGetProgramiv(thing, check_type, &success);
|
|
|
|
if(!success) {
|
|
glGetProgramInfoLog(thing, 512, NULL, infoLog);
|
|
dbc::sentinel(std::format("ERROR: Program {} compile failed: {}", what, infoLog));
|
|
}
|
|
} else {
|
|
glGetShaderiv(thing, check_type, &success);
|
|
|
|
if(!success) {
|
|
glGetShaderInfoLog(thing, 512, NULL, infoLog);
|
|
dbc::sentinel(std::format("ERROR: Shader {} compile failed: {}", what, infoLog));
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int Shader::load_shader(const std::string& filename, GLenum shader_type) {
|
|
dbc::log($F("Loading shader {}", filename));
|
|
dbc::check(fs::exists(filename),
|
|
std::format("shader file {} does not exist", filename));
|
|
|
|
// create the shader
|
|
std::string shader_code = read_file(filename);
|
|
const char* shader_code_ptr = shader_code.c_str();
|
|
|
|
int shader_id = glCreateShader(shader_type);
|
|
glShaderSource(shader_id, 1, &shader_code_ptr, NULL);
|
|
glCompileShader(shader_id);
|
|
|
|
check_error(filename, shader_id, GL_COMPILE_STATUS);
|
|
|
|
// check compile error
|
|
return shader_id;
|
|
}
|
|
|
|
Shader::Shader(const std::string& vertexPath, const std::string& fragmentPath) {
|
|
unsigned int vertex = load_shader(vertexPath, GL_VERTEX_SHADER);
|
|
unsigned int fragment = load_shader(fragmentPath, GL_FRAGMENT_SHADER);
|
|
|
|
ID = glCreateProgram();
|
|
glAttachShader(ID, vertex);
|
|
glAttachShader(ID, fragment);
|
|
glLinkProgram(ID);
|
|
|
|
check_error("link", ID, GL_LINK_STATUS);
|
|
|
|
glDeleteShader(vertex);
|
|
glDeleteShader(fragment);
|
|
}
|
|
|
|
void Shader::use() const {
|
|
glUseProgram(ID);
|
|
}
|
|
|
|
void Shader::cleanup() {
|
|
glDeleteProgram(ID);
|
|
}
|
|
|
|
void Shader::setBool(const std::string &name, bool value) const {
|
|
auto uniform = glGetUniformLocation(ID, name.c_str());
|
|
glUniform1i(uniform, (int)value);
|
|
}
|
|
|
|
void Shader::setInt(const std::string &name, int value) const {
|
|
auto uniform = glGetUniformLocation(ID, name.c_str());
|
|
glUniform1i(uniform, value);
|
|
}
|
|
|
|
void Shader::setFloat(const std::string &name, float value) const {
|
|
auto uniform = glGetUniformLocation(ID, name.c_str());
|
|
glUniform1f(uniform, value);
|
|
}
|
|
|
|
void Shader::setVec4(const std::string &name, float v1, float v2, float v3, float v4) const {
|
|
auto uniform = glGetUniformLocation(ID, name.c_str());
|
|
glUniform4f(uniform, v1, v2, v3, v4);
|
|
}
|
|
|
|
void Shader::setVec4(const std::string &name, const glm::vec4& value) const
|
|
{
|
|
auto uniform = glGetUniformLocation(ID, name.c_str());
|
|
glUniform4fv(uniform, 1, &value[0]);
|
|
}
|
|
|
|
void Shader::setVec3(const std::string &name, const glm::vec3& value) const
|
|
{
|
|
auto uniform = glGetUniformLocation(ID, name.c_str());
|
|
glUniform3fv(uniform, 1, &value[0]);
|
|
}
|
|
|
|
void Shader::setVec3(const std::string &name, float v1, float v2, float v3) const {
|
|
auto uniform = glGetUniformLocation(ID, name.c_str());
|
|
glUniform3f(uniform, v1, v2, v3);
|
|
}
|
|
|
|
void Shader::setMat4(const std::string &name, const glm::mat4& mat) const {
|
|
unsigned int loc = glGetUniformLocation(ID, name.c_str());
|
|
glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(mat));
|
|
}
|
|
|
|
void Shader::apply_material(const Material& material) {
|
|
setVec3("material.ambient", material.ambient);
|
|
setFloat("material.shininess", material.shininess);
|
|
}
|
|
|
|
void Shader::apply_lighting(const Lighting& lighting) {
|
|
setInt("pointLightCount", lighting.positioned.size());
|
|
setInt("dirLightCount", lighting.directional.size());
|
|
|
|
for(size_t i = 0; i < lighting.directional.size(); i++) {
|
|
apply_dir_light(lighting.directional[i], i);
|
|
}
|
|
|
|
for(size_t i = 0; i < lighting.positioned.size(); i++) {
|
|
apply_point_light(lighting.positioned[i], i);
|
|
}
|
|
|
|
// set the first spotlight to the camera light if given
|
|
size_t i = 0;
|
|
|
|
// camera is optional, so only add if used
|
|
if(lighting.camera.on) {
|
|
apply_spot_light(lighting.camera, 0);
|
|
i++;
|
|
}
|
|
|
|
// gross but that's fine for now
|
|
for(; i < lighting.spot.size(); i++) {
|
|
// need to be off by one because the camera is a spot light
|
|
if(lighting.spot[i].on) {
|
|
apply_spot_light(lighting.spot[i], i+1);
|
|
}
|
|
}
|
|
|
|
// need +1 for the camera spot light
|
|
setInt("spotLightCount", (int)i);
|
|
}
|
|
|
|
|
|
void Shader::apply_dir_light(const Light& light, size_t index) {
|
|
dbc::check(index < MAX_LIGHTS, "too many directional lights");
|
|
|
|
// nasty, this needs to go
|
|
setVec3(std::format("dirLights[{}].direction", index), light.direction);
|
|
setVec3(std::format("dirLights[{}].ambient", index), light.ambient);
|
|
setVec3(std::format("dirLights[{}].diffuse", index), light.diffuse);
|
|
setVec3(std::format("dirLights[{}].specular", index), light.specular);
|
|
}
|
|
|
|
void Shader::apply_point_light(const Light& light, size_t index) {
|
|
dbc::check(index < MAX_LIGHTS, "too many positioned lights");
|
|
|
|
// disgusting, crafting a string on every render?
|
|
setVec3(std::format("pointLights[{}].position", index), light.position);
|
|
setVec3(std::format("pointLights[{}].ambient", index), light.ambient);
|
|
setVec3(std::format("pointLights[{}].diffuse", index), light.diffuse);
|
|
setVec3(std::format("pointLights[{}].specular", index), light.specular);
|
|
|
|
setFloat(std::format("pointLights[{}].constant", index), light.constant);
|
|
setFloat(std::format("pointLights[{}].linear", index), light.linear);
|
|
setFloat(std::format("pointLights[{}].quadratic", index), light.quadratic);
|
|
}
|
|
|
|
void Shader::apply_spot_light(const Light& light, size_t index) {
|
|
dbc::check(index < MAX_LIGHTS, "too many spot lights");
|
|
|
|
// find a way to not make strings all the time
|
|
setVec3(std::format("spotLights[{}].position", index), light.position);
|
|
setVec3(std::format("spotLights[{}].direction", index), light.direction);
|
|
setVec3(std::format("spotLights[{}].ambient", index), light.ambient);
|
|
setVec3(std::format("spotLights[{}].diffuse", index), light.diffuse);
|
|
setVec3(std::format("spotLights[{}].specular", index), light.specular);
|
|
setFloat(std::format("spotLights[{}].constant", index), light.constant);
|
|
setFloat(std::format("spotLights[{}].linear", index), light.linear);
|
|
setFloat(std::format("spotLights[{}].quadratic", index), light.quadratic);
|
|
setFloat(std::format("spotLights[{}].cut_off", index), glm::cos(glm::radians(light.cut_off)));
|
|
setFloat(std::format("spotLights[{}].outer_cut_off", index), glm::cos(glm::radians(light.outer_cut_off)));
|
|
}
|