You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

242 lines
7.4 KiB

#include "ShaderProgram.h"
#include "ExternalLib/tinydir.h"
//#include <experimental/filesystem> // TODO REPLACE tinydir by this once gcc implements it correctly ...
#include "ExternalLib/glad.h"
#define DEBUG_SHADER_PROGRAM 1
/******************
* Constructors *
******************/
ShaderProgram::ShaderProgram()
: programId(0), vertexShaderList(std::list<Shader>()),
fragmentShaderList(std::list<Shader>()), geometryShaderList(std::list<Shader>()),
shaderDirectoryPath("")
{}
/******************
* Destructor *
******************/
ShaderProgram::~ShaderProgram()
{
// DO NOT CALL ANY GL CONTEXT DEPENDANT METHOD HERE !
// AS ANONYMOUS INSTANCES OF SHADERPROGRAM COULD PRODUCE UNEXPECTED BEHAVIORS
programId = 0;
vertexShaderList.clear();
fragmentShaderList.clear();
geometryShaderList.clear();
}
/*******************
* Methods *
*******************/
void ShaderProgram::attachShaders(std::string const &shaderDirectoryPath_)
{
/**
* \brief Attach every available shader (.vert/.frag/.geom) within shaderDirectoryPath
* \param shaderDirectory : absolute path of the directory storing shaders (at least one vertex and fragment)
*/
shaderDirectoryPath = shaderDirectoryPath_;
// Verify state of programId
if (programId == 0)
std::cerr << "ERROR::ATTACHSHADER : Trying to attach shaders to unitialized ShaderProgram" << std::endl;
// Making sure to detach already existing shaders
detachShaders();
// Explore and parse shaderDirectory and create Shader according to the extension of found files
tinydir_dir shaderDirectory;
tinydir_open(&shaderDirectory, shaderDirectoryPath.c_str());
while (shaderDirectory.has_next)
{
tinydir_file file;
tinydir_readfile(&shaderDirectory, &file);
if (!file.is_dir)
{
if ( !strcmp(file.extension, "vert") )
{
if (DEBUG_SHADER_PROGRAM)
std::cout << "ATTACHING SHADER : " << shaderDirectoryPath << file.name<< std::endl;
Shader vertexShader(GL_VERTEX_SHADER);
vertexShader.loadFromFile(file.path);
vertexShader.compile();
vertexShaderList.push_back(vertexShader);
glAttachShader(programId, vertexShader.getId());
}
else if ( !strcmp(file.extension, "frag") )
{
if (DEBUG_SHADER_PROGRAM)
std::cout << "ATTACHING SHADER : " << shaderDirectoryPath << file.name<< std::endl;
Shader fragmentShader(GL_FRAGMENT_SHADER);
fragmentShader.loadFromFile(file.path);
fragmentShader.compile();
fragmentShaderList.push_back(fragmentShader);
glAttachShader(programId, fragmentShader.getId());
}
else if ( !strcmp(file.extension, "geom") )
{
if (DEBUG_SHADER_PROGRAM)
std::cout << "ATTACHING SHADER : " << shaderDirectoryPath << file.name<< std::endl;
Shader geometryShader(GL_GEOMETRY_SHADER);
geometryShader.loadFromFile(file.path);
geometryShader.compile();
geometryShaderList.push_back(geometryShader);
glAttachShader(programId, geometryShader.getId());
}
}
tinydir_next(&shaderDirectory);
}
tinydir_close(&shaderDirectory);
}
void ShaderProgram::detachShaders()
{
/**
* \brief Detach and set for deletion every shaders listed (needs a reload() for changes to be effective)
*/
for (auto& shader: vertexShaderList)
{
// Detach Shaders
glDetachShader(programId, shader.getId());
// Delete Shaders / "unload Shader" / mark them for deletion for when shader program is not active
shader.unload(); // mark shader for deletion
}
vertexShaderList.clear(); // empty vertex Shader List
for (auto& shader: fragmentShaderList)
{
// Detach Shaders
glDetachShader(programId, shader.getId());
// Delete Shaders / "unload Shader" / mark them for deletion for when shader program is not active
shader.unload(); // mark shader for deletion
}
fragmentShaderList.clear(); // empty fragment Shader List
for (auto& shader: geometryShaderList)
{
// Detach Shaders
glDetachShader(programId, shader.getId());
// Delete Shaders / "unload Shader" / mark them for deletion for when shader program is not active
shader.unload(); // mark shader for deletion
}
geometryShaderList.clear(); // empty geometry Shader List
}
void ShaderProgram::initGL()
{
// verifiy if ShaderProgram got a GPU context / ID. If not create it
if (programId == 0)
programId = glCreateProgram();
else
std::cerr << "ERROR::SHADERPROGRAM initGL : Multiple initGL calls " << std::endl;
}
void ShaderProgram::destroyGL()
{
// verifiy if ShaderProgram got a GPU context / ID. If there is one, delete it
if (programId != 0)
{
glDeleteProgram(programId);
programId = 0;
}
else
std::cerr << "ERROR::SHADERPROGRAM DESTROYGL : Trying to delete programId 0" << std::endl;
}
bool ShaderProgram::linkProgram()
{
/**
* \brief link the shaderProgram && verify there is at least a frag+vert && delete now obsolete glShaders instances
*/
// Verify state of programId
if (programId == 0)
std::cerr << "ERROR::LINKPROGRAM : Trying to link unitialized ShaderProgram" << std::endl;
// verify that there is at least a frag and a vert shader
if ( vertexShaderList.size() >= 1 && fragmentShaderList.size() >= 1 )
{
// there is at least the minimals fragment and vertex shaders
// => link the shaderProgram
glLinkProgram(programId);
// check for compiling errors
GLint success;
GLchar infoLog[512];
glGetProgramiv(programId, GL_LINK_STATUS, &success);
if(!success) // if something went wrong
{
glGetProgramInfoLog(programId, 512, nullptr, infoLog);
std::cerr << "ERROR::SHADERPROGRAM LINKING \n" << infoLog << std::endl;
return false;
}
else
{
// detach shaders, set them for deletion and clear the shader lists
// shaderProgram is already linked, compiled and running on the GPU
detachShaders(); // deleting now useless attached Shaders code ( included in genereated ShaderProgram )
return true;
}
}
else
{
// we've iterated over the whole shaderList without finding vertex nor fragment shader
std::cerr << "ERROR::SHADERPROGRAM LINKING" << std::endl;
std::cerr << "Didn't find any vertex or/and fragment shader attached to this ShaderProgram" << std::endl;
return false;
}
}
void ShaderProgram::enable()
{
// Verify state of programId
if (programId == 0)
std::cerr << "ERROR::ENABLE : Trying to enable unitialized ShaderProgram" << std::endl;
glUseProgram(programId);
}
void ShaderProgram::disable()
{
glUseProgram(0);
}
GLuint ShaderProgram::getId() const
{
return programId;
}
bool ShaderProgram::reload()
{
disable();
destroyGL();
initGL();
attachShaders(shaderDirectoryPath);
if ( ! linkProgram() )
return false;
enable();
return true;
}