1#include <Engine/Data/ShaderProgram.hpp>
2#include <Engine/OpenGL.hpp>
4#include <glbinding/gl/enum.h>
5#include <globjects/NamedString.h>
6#include <globjects/Program.h>
7#include <globjects/Shader.h>
8#include <globjects/Texture.h>
9#include <globjects/base/File.h>
10#include <globjects/base/StaticStringSource.h>
12#include <Core/Utils/Log.hpp>
13#include <Engine/Data/Texture.hpp>
23using namespace Core::Utils;
27GLenum getTypeAsGLEnum( ShaderType type ) {
29 case ShaderType_VERTEX:
30 return GL_VERTEX_SHADER;
31 case ShaderType_FRAGMENT:
32 return GL_FRAGMENT_SHADER;
33 case ShaderType_GEOMETRY:
34 return GL_GEOMETRY_SHADER;
35 case ShaderType_TESS_EVALUATION:
36 return GL_TESS_EVALUATION_SHADER;
37 case ShaderType_TESS_CONTROL:
38 return GL_TESS_CONTROL_SHADER;
41 case ShaderType_COMPUTE:
42 return GL_COMPUTE_SHADER;
45 CORE_ERROR(
"Wrong ShaderType" );
52ShaderType getGLenumAsType( GLenum type ) {
54 case GL_VERTEX_SHADER:
55 return ShaderType_VERTEX;
56 case GL_FRAGMENT_SHADER:
57 return ShaderType_FRAGMENT;
58 case GL_GEOMETRY_SHADER:
59 return ShaderType_GEOMETRY;
60 case GL_TESS_EVALUATION_SHADER:
61 return ShaderType_TESS_EVALUATION;
62 case GL_TESS_CONTROL_SHADER:
63 return ShaderType_TESS_CONTROL;
65 case GL_COMPUTE_SHADER:
66 return ShaderType_COMPUTE;
69 CORE_ERROR(
"Wrong GLenum" );
73 return ShaderType_COUNT;
76ShaderProgram::ShaderProgram() : m_program { nullptr } {
77 std::generate( m_shaderObjects.begin(), m_shaderObjects.end(), []() {
78 return std::pair<bool, std::unique_ptr<globjects::Shader>> { false, nullptr };
80 std::fill( m_shaderSources.begin(), m_shaderSources.end(),
nullptr );
83ShaderProgram::ShaderProgram(
const ShaderConfiguration& config ) : ShaderProgram() {
87ShaderProgram::~ShaderProgram() {
91 for (
auto& s : m_shaderObjects ) {
92 s.second.reset(
nullptr );
94 for (
auto& s : m_shaderSources ) {
97 m_program.reset(
nullptr );
100void ShaderProgram::loadShader( ShaderType type,
107 if ( type == ShaderType_COMPUTE ) {
108 LOG( logERROR ) <<
"No compute shader on OsX !";
119 if ( type == ShaderType_VERTEX ) {
121 "out gl_PerVertex {\n"
122 " vec4 gl_Position;\n"
123 " float gl_PointSize;\n"
124 " float gl_ClipDistance[];\n"
127 else { shaderHeader =
std::string( version +
"\n\n" ); }
132 return std::move( a ) + b + std::string(
"\n" );
141 if ( b.second == type ) { return std::move( a ) + b.first + std::string(
"\n" ); }
147 auto loadedSource = globjects::Shader::sourceFromFile( name );
148 fullsource = globjects::Shader::sourceFromString( shaderHeader + loadedSource->string() );
150 else { fullsource = globjects::Shader::sourceFromString( shaderHeader + name ); }
153 auto shaderSource = globjects::Shader::applyGlobalReplacements( fullsource.get() );
158 std::string preprocessedSource = preprocessIncludes( name, shaderSource->string(), 0 );
160 auto ptrSource = globjects::Shader::sourceFromString( preprocessedSource );
163 auto ptrSource = globjects::Shader::sourceFromString( shaderSource->string() );
165 addShaderFromSource( type,
std::move( ptrSource ), name, fromFile );
173 auto shader = globjects::Shader::create( getTypeAsGLEnum( type ) );
175 shader->setName( name );
176 shader->setSource( ptrSource.get() );
180 m_shaderObjects[type].first = fromFile;
181 m_shaderObjects[type].second.swap( shader );
183 m_shaderSources[type].swap( ptrSource );
189 m_configuration = shaderConfig;
191 CORE_ERROR_IF( m_configuration.isComplete(),
192 (
"Shader program " + shaderConfig.m_name +
193 " is incomplete (e.g. misses vertex or fragment shader)." )
196 for (
size_t i = 0; i < ShaderType_COUNT; ++i ) {
197 if ( !m_configuration.m_shaders[i].first.empty() ) {
199 m_configuration.m_shaders[i].first,
200 m_configuration.getProperties(),
201 m_configuration.getIncludes(),
202 m_configuration.m_shaders[i].second,
203 m_configuration.m_version );
210void ShaderProgram::link() {
211 m_program = globjects::Program::create();
213 for (
unsigned int i = 0; i < ShaderType_COUNT; ++i ) {
214 if ( m_shaderObjects[i].second ) { m_program->attach( m_shaderObjects[i].second.get() ); }
217 m_program->setParameter( GL_PROGRAM_SEPARABLE, GL_TRUE );
222 auto total = GLuint( m_program->get( GL_ACTIVE_UNIFORMS ) );
223 textureUnits.clear();
225 for ( GLuint i = 0; i < total; ++i ) {
226 auto name = m_program->getActiveUniformName( i );
227 auto type = m_program->getActiveUniform( i, GL_UNIFORM_TYPE );
230 if ( type == GL_SAMPLER_1D || type == GL_SAMPLER_2D || type == GL_SAMPLER_3D ||
231 type == GL_SAMPLER_CUBE || type == GL_SAMPLER_1D_SHADOW ||
232 type == GL_SAMPLER_2D_SHADOW || type == GL_SAMPLER_CUBE_SHADOW ||
233 type == GL_SAMPLER_2D_RECT || type == GL_SAMPLER_2D_RECT_SHADOW ||
234 type == GL_SAMPLER_1D_ARRAY || type == GL_SAMPLER_2D_ARRAY ||
235 type == GL_SAMPLER_BUFFER || type == GL_SAMPLER_1D_ARRAY_SHADOW ||
236 type == GL_SAMPLER_2D_ARRAY_SHADOW || type == GL_INT_SAMPLER_1D ||
237 type == GL_INT_SAMPLER_2D || type == GL_INT_SAMPLER_3D ||
238 type == GL_INT_SAMPLER_CUBE || type == GL_INT_SAMPLER_2D_RECT ||
239 type == GL_INT_SAMPLER_1D_ARRAY || type == GL_INT_SAMPLER_2D_ARRAY ||
240 type == GL_INT_SAMPLER_BUFFER || type == GL_UNSIGNED_INT_SAMPLER_1D ||
241 type == GL_UNSIGNED_INT_SAMPLER_2D || type == GL_UNSIGNED_INT_SAMPLER_3D ||
242 type == GL_UNSIGNED_INT_SAMPLER_CUBE || type == GL_UNSIGNED_INT_SAMPLER_2D_RECT ||
243 type == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY || type == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY ||
244 type == GL_UNSIGNED_INT_SAMPLER_BUFFER || type == GL_SAMPLER_CUBE_MAP_ARRAY ||
245 type == GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW || type == GL_INT_SAMPLER_CUBE_MAP_ARRAY ||
246 type == GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY || type == GL_SAMPLER_2D_MULTISAMPLE ||
247 type == GL_INT_SAMPLER_2D_MULTISAMPLE ||
248 type == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE ||
249 type == GL_SAMPLER_2D_MULTISAMPLE_ARRAY ||
250 type == GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY ||
251 type == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY ) {
252 auto location = m_program->getUniformLocation( name );
253 textureUnits[name] = TextureBinding( texUnit++, location );
258void ShaderProgram::bind()
const {
262void ShaderProgram::validate()
const {
263 m_program->validate();
264 if ( !m_program->isValid() ) { LOG( logDEBUG ) << m_program->infoLog(); }
267void ShaderProgram::unbind()
const {
268 m_program->release();
271void ShaderProgram::reload() {
272 for (
auto& s : m_shaderObjects ) {
273 if ( s.second !=
nullptr ) {
274 if ( s.first ) { LOG( logDEBUG ) <<
"Reloading shader " << s.second->name(); }
276 m_program->detach( s.second.get() );
277 loadShader( getGLenumAsType( s.second->type() ),
279 m_configuration.getProperties(),
280 m_configuration.getIncludes(),
282 m_configuration.m_version );
289ShaderConfiguration ShaderProgram::getBasicConfiguration()
const {
290 ShaderConfiguration basicConfig;
292 basicConfig.m_shaders = m_configuration.m_shaders;
293 basicConfig.m_name = m_configuration.m_name;
299void ShaderProgram::setUniform(
const char* name,
const Core::Vector2d& value )
const {
300 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
304void ShaderProgram::setUniform(
const char* name,
const Core::Vector3d& value )
const {
305 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
309void ShaderProgram::setUniform(
const char* name,
const Core::Vector4d& value )
const {
310 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
314void ShaderProgram::setUniform(
const char* name,
const Core::Matrix2d& value )
const {
315 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
319void ShaderProgram::setUniform(
const char* name,
const Core::Matrix3d& value )
const {
320 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
324void ShaderProgram::setUniform(
const char* name,
const Core::Matrix4d& value )
const {
325 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
329void ShaderProgram::setUniform(
const char* name,
const Scalar& value )
const {
330 m_program->setUniform( name,
static_cast<GL_SCALAR_PLAIN
>( value ) );
335void scalarVectorAdapter( globjects::Program* prog,
342 []( Scalar c ) -> GL_SCALAR_PLAIN { return c; } );
343 prog->setUniform( name, convertedValue );
348void scalarVectorAdapter( globjects::Program* prog,
351 prog->setUniform( name, value );
355void ShaderProgram::setUniform(
const char* name,
const std::vector<Scalar>& value )
const {
356 scalarVectorAdapter<Scalar>( m_program.get(), name, value );
359void ShaderProgram::setUniform(
const char* name, Texture* tex,
int texUnit )
const {
360 tex->bind( texUnit );
362 m_program->setUniform( name, texUnit );
365void ShaderProgram::setUniformTexture(
const char* name,
Texture* tex )
const {
366 auto itr = textureUnits.find(
std::string( name ) );
367 if ( itr != textureUnits.end() ) {
368 tex->
bind( itr->second.m_texUnit );
369 m_program->setUniform( itr->second.m_location, itr->second.m_texUnit );
373globjects::Program* ShaderProgram::getProgramObject()
const {
374 return m_program.get();
385 CORE_ERROR_IF( level < 32,
"Shader inclusion depth limit reached." );
392 static const std::regex reg(
"^[ ]*#[ ]*include[ ]+[\"<](.*)[\">].*" );
401 auto includeNameString =
402 globjects::NamedString::getFromRegistry(
std::string(
"/" ) + match[1].str() );
403 if ( includeNameString !=
nullptr ) {
406 preprocessIncludes( match[1].str(), includeNameString->string(), level + 1, 0 );
409 LOG( logWARNING ) <<
"Cannot open included file " << match[1].
str() <<
" at line"
410 << nline <<
" of file " << name <<
". Ignored.";
439 for (
const auto& l : finalStrings ) {
441 result.append(
"\n" );
T back_inserter(T... args)
Represent a Texture of the engine.
void bind(int unit=-1)
Bind the texture to GPU texture unit to enable its use in a shader. Need active OpenGL context.
hepler function to manage enum as underlying types in VariableSet
T regex_search(T... args)