Onyx-Engine/src/core/systems/Shader.cpp
OusmBlueNinja d107be98d4 Improves asset loading and shader management
Improves asset management by adding a shutdown procedure that safely terminates the asset scanning thread. This prevents crashes and ensures a clean exit.

Enhances shader management by introducing shader naming and improved error logging. This makes debugging easier. Also renames the sprite shader.

Fixes a minor bug related to sprite drawing position.

Adds Git mappings for submodules.
2025-05-22 09:25:29 -05:00

183 lines
5.1 KiB
C++

#include "Shader.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include "systems/Logger.h"
namespace OX
{
Shader::Shader(const std::string &vertexPath, const std::string &fragmentPath)
{
LoadFromFiles(vertexPath, fragmentPath);
}
bool Shader::LoadFromFiles(const std::string &vertexPath, const std::string &fragmentPath)
{
m_vertexPath = vertexPath;
m_fragmentPath = fragmentPath;
std::string vertexSrc = ReadFile(vertexPath);
std::string fragmentSrc = ReadFile(fragmentPath);
try {
m_vertTimestamp = std::filesystem::last_write_time(vertexPath);
m_fragTimestamp = std::filesystem::last_write_time(fragmentPath);
} catch (...) {
std::cerr << "[Shader] Warning: Could not get file timestamps.\n";
}
return LoadFromSource(vertexSrc, fragmentSrc);
}
bool Shader::LoadFromSource(const std::string &vertexSrc, const std::string &fragmentSrc)
{
GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertexSrc);
GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragmentSrc);
if (!vertexShader || !fragmentShader) return false;
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
char info[512];
glGetProgramInfoLog(program, 512, nullptr, info);
Logger::LogError("'%s' Linking failed: '%s'", m_name.c_str(), info);
glDeleteProgram(program);
return false;
}
if (m_programID)
glDeleteProgram(m_programID);
m_programID = program;
m_uniformCache.clear();
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
Logger::LogDebug("Shader '%s' Loaded", m_name.c_str());
return true;
}
void Shader::Reload()
{
OX_PROFILE_FUNCTION();
LoadFromFiles(m_vertexPath, m_fragmentPath);
}
void Shader::Use() const
{
glUseProgram(m_programID);
}
GLuint Shader::CompileShader(GLenum type, const std::string &source)
{
OX_PROFILE_FUNCTION();
GLuint shader = glCreateShader(type);
const char *src = source.c_str();
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char info[512];
glGetShaderInfoLog(shader, 512, nullptr, info);
Logger::LogDebug("Failed to Compile Shader: '%s'", m_name.c_str());
return 0;
}
return shader;
}
void Shader::CheckHotReload()
{
OX_PROFILE_FUNCTION();
if (m_vertexPath.empty() || m_fragmentPath.empty()) {
return;
}
try {
OX_PROFILE_LABEL("Get Write Time");
auto vertTime = std::filesystem::last_write_time(m_vertexPath);
auto fragTime = std::filesystem::last_write_time(m_fragmentPath);
if (vertTime != m_vertTimestamp || fragTime != m_fragTimestamp) {
std::cout << "[Shader] Reloading shader: " << m_vertexPath << " / " << m_fragmentPath << "\n";
Reload();
}
} catch (const std::exception &e) {
Logger::LogError("Failed to Reload Shader '%s', %s", m_name.c_str(), e.what());
}
}
std::string Shader::ReadFile(const std::string &path)
{
if (!std::filesystem::exists(path)) {
std::ofstream file(path, std::ios::app);
}
std::ifstream file(path);
std::stringstream ss;
ss << file.rdbuf();
return ss.str();
}
GLint Shader::GetUniformLocation(const std::string &name)
{
if (m_uniformCache.contains(name))
return m_uniformCache[name];
GLint location = glGetUniformLocation(m_programID, name.c_str());
m_uniformCache[name] = location;
return location;
}
void Shader::SetInt(const std::string &name, int value)
{
glUniform1i(GetUniformLocation(name), value);
}
void Shader::SetFloat(const std::string &name, float value)
{
glUniform1f(GetUniformLocation(name), value);
}
void Shader::SetVec2(const std::string &name, const glm::vec2 &value)
{
glUniform2fv(GetUniformLocation(name), 1, &value[0]);
}
void Shader::SetVec3(const std::string &name, const glm::vec3 &value)
{
glUniform3fv(GetUniformLocation(name), 1, &value[0]);
}
void Shader::SetVec4(const std::string &name, const glm::vec4 &value)
{
glUniform4fv(GetUniformLocation(name), 1, &value[0]);
}
void Shader::SetMat4(const std::string &name, const glm::mat4 &value)
{
glUniformMatrix4fv(GetUniformLocation(name), 1, GL_FALSE, &value[0][0]);
}
void Shader::SetBool(const std::string &name, bool value)
{
glUniform1i(GetUniformLocation(name), (int) value);
}
}