//
// shader.h
// J
//
// Created by Joshua Moerman on 7/01/11.
// Copyright 2011 Vadovas. All rights reserved.
//
# ifndef SHADER_H
# define SHADER_H
# include <iostream>
# include <fstream>
# include <stdexcept>
# include <map>
# include <vector>
# include <iterator>
# include "basic.h"
# include "array.h"
// TODO: do glValidateProgram, like in the OpenGL template (see bottom)
// TODO: add error checking at set_uniforms?
// TODO: use an uniform map? (benchmark first!)
// TODO: allow arrays is uniforms (now all counts are 1)
// TODO: matrix attributes are not handled well (they take up 4 indices in the attributes)
// NOTE: there is only 1 set_uniform for std::array...
namespace J {
class shader {
// NOTE: it could be hardcoded to use vertex_shader and fragment_shader, for ShaderMap.
typedef std : : map < GLenum , GLuint > ShaderMap ; // shader_type -> shader_name
typedef std : : map < std : : string , GLuint > AttributeMap ; // attribute_name -> attribute_index
GLuint program ;
ShaderMap shaders ;
AttributeMap attributes ;
public :
// ***********
// constructor
shader ( const std : : string & vertex_shader_filename , const std : : string & fragment_shader_filename , const std : : vector < std : : string > & attributes_to_use = std : : vector < std : : string > ( ) ) :
program ( 0 ) , shaders ( ) , attributes ( ) {
program = glCreateProgram ( ) ;
if ( program = = 0 ) {
throw std : : runtime_error ( " Program couldn't be created " ) ;
}
{ // vertex shader
// NOTE: C++03 mandates .c_str(), ugly, nothing to do about it...
std : : ifstream vertex_shader ( vertex_shader_filename . c_str ( ) ) ;
if ( ! vertex_shader )
throw std : : runtime_error ( vertex_shader_filename + " couldn't be opened " ) ;
compile_shader ( vertex_shader , GL_VERTEX_SHADER ) ;
}
{ // fragment shader
std : : ifstream fragment_shader ( fragment_shader_filename . c_str ( ) ) ;
if ( ! fragment_shader )
throw std : : runtime_error ( fragment_shader_filename + " couldn't be opened " ) ;
compile_shader ( fragment_shader , GL_FRAGMENT_SHADER ) ;
}
for ( unsigned int i = 0 ; i < attributes_to_use . size ( ) ; + + i ) {
glBindAttribLocation ( program , i , attributes_to_use [ i ] . c_str ( ) ) ;
attributes [ attributes_to_use [ i ] ] = i ;
}
link_program ( ) ;
check_error ( ) ;
}
~ shader ( ) {
begin ( ) ;
for ( ShaderMap : : const_iterator i = shaders . begin ( ) ; i ! = shaders . end ( ) ; + + i ) {
ShaderMap : : value_type const & it = * i ;
GLuint shader = it . second ;
glDetachShader ( program , shader ) ;
glDeleteShader ( shader ) ;
// NOTE: If a shader object to be deleted is attached to a program object, it will be flagged for deletion, but it will not be deleted until it is no longer attached to any program object
}
end ( ) ;
glDeleteProgram ( program ) ;
}
// *****
// usage
void begin ( ) {
glUseProgram ( program ) ;
}
void end ( ) {
glUseProgram ( 0 ) ;
}
// *****************
// attribute setters
void set_attribute ( const std : : string & name , GLint size , GLenum type , GLboolean normalized , GLsizei stride , const GLvoid * ptr ) {
const GLuint index = attributes [ name ] ;
glVertexAttribPointer ( index , size , type , normalized , stride , ptr ) ;
glEnableVertexAttribArray ( index ) ;
}
// ***************
// uniform setters
// float vectors (multiple values)
void set_uniform ( char const * name , float x ) const {
glUniform1f ( glGetUniformLocation ( program , name ) , x ) ;
}
void set_uniform ( char const * name , float x , float y ) const {
glUniform2f ( glGetUniformLocation ( program , name ) , x , y ) ;
}
void set_uniform ( char const * name , float x , float y , float z ) const {
glUniform3f ( glGetUniformLocation ( program , name ) , x , y , z ) ;
}
void set_uniform ( char const * name , float x , float y , float z , float w ) const {
glUniform4f ( glGetUniformLocation ( program , name ) , x , y , z , w ) ;
}
// integer vectors (multiple values)
void set_uniform ( char const * name , int x ) const {
glUniform1i ( glGetUniformLocation ( program , name ) , x ) ;
}
void set_uniform ( char const * name , int x , int y ) const {
glUniform2i ( glGetUniformLocation ( program , name ) , x , y ) ;
}
void set_uniform ( char const * name , int x , int y , int z ) const {
glUniform3i ( glGetUniformLocation ( program , name ) , x , y , z ) ;
}
void set_uniform ( char const * name , int x , int y , int z , int w ) const {
glUniform4i ( glGetUniformLocation ( program , name ) , x , y , z , w ) ;
}
// Float vectors (array)
void set_uniform ( char const * name , const GLfloat ( & value ) [ 4 ] ) const {
glUniform4fv ( glGetUniformLocation ( program , name ) , 1 , value ) ;
}
void set_uniform ( char const * name , const GLfloat ( & value ) [ 3 ] ) const {
glUniform3fv ( glGetUniformLocation ( program , name ) , 1 , value ) ;
}
void set_uniform ( char const * name , const GLfloat ( & value ) [ 2 ] ) const {
glUniform2fv ( glGetUniformLocation ( program , name ) , 1 , value ) ;
}
void set_uniform ( char const * name , const GLfloat ( & value ) [ 1 ] ) const {
glUniform1fv ( glGetUniformLocation ( program , name ) , 1 , value ) ;
}
// Integer vectors (array)
void set_uniform ( char const * name , const GLint ( & value ) [ 4 ] ) const {
glUniform4iv ( glGetUniformLocation ( program , name ) , 1 , value ) ;
}
void set_uniform ( char const * name , const GLint ( & value ) [ 3 ] ) const {
glUniform3iv ( glGetUniformLocation ( program , name ) , 1 , value ) ;
}
void set_uniform ( char const * name , const GLint ( & value ) [ 2 ] ) const {
glUniform2iv ( glGetUniformLocation ( program , name ) , 1 , value ) ;
}
void set_uniform ( char const * name , const GLint ( & value ) [ 1 ] ) const {
glUniform1iv ( glGetUniformLocation ( program , name ) , 1 , value ) ;
}
// matrices (in ES2 only square matrices)
void set_uniform ( char const * name , const GLfloat ( & value ) [ 16 ] , GLboolean trans ) const {
glUniformMatrix4fv ( glGetUniformLocation ( program , name ) , 1 , trans , value ) ;
}
void set_uniform ( char const * name , const std : : array < GLfloat , 16 > & value , GLboolean trans ) const {
glUniformMatrix4fv ( glGetUniformLocation ( program , name ) , 1 , trans , & value [ 0 ] ) ;
}
void set_uniform ( char const * name , const GLfloat ( & value ) [ 9 ] , GLboolean trans ) const {
glUniformMatrix3fv ( glGetUniformLocation ( program , name ) , 1 , trans , value ) ;
}
void set_uniform ( char const * name , const GLfloat ( & value ) [ 4 ] , GLboolean trans ) const {
glUniformMatrix2fv ( glGetUniformLocation ( program , name ) , 1 , trans , value ) ;
}
// textures (see todo above)
void set_texture ( const char * name , GLenum target , GLuint tex , int textureLocation , GLenum filter = GL_LINEAR ) const {
glActiveTexture ( GL_TEXTURE0 + textureLocation ) ;
glBindTexture ( target , tex ) ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , filter ) ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , filter ) ;
set_uniform ( name , textureLocation ) ;
}
// *********
// validator
void validate ( ) {
# if defined(DEBUG)
GLint status ;
glValidateProgram ( program ) ;
glGetProgramiv ( program , GL_VALIDATE_STATUS , & status ) ;
if ( status = = GL_FALSE ) {
std : : cerr < < " validator reports: " < < std : : endl ;
show_program_log ( ) ;
throw std : : runtime_error ( " Program not valid " ) ;
}
# endif
}
private :
// *******************
// compiling / linking
void compile_shader ( std : : istream & file , GLenum type ) {
// get the c-string, the shortest way (not fastest)
std : : string buffer ( ( std : : istreambuf_iterator < char > ( file ) ) , std : : istreambuf_iterator < char > ( ) ) ;
const char * sptr = buffer . c_str ( ) ;
int ssize = buffer . size ( ) ;
// create and compile shader
GLuint shader = glCreateShader ( type ) ;
glShaderSource ( shader , 1 , & sptr , & ssize ) ;
glCompileShader ( shader ) ;
check_compile_status ( shader , buffer ) ;
// put it in tha map
shaders [ type ] = shader ;
}
void link_program ( ) {
// attach all shaders
for ( ShaderMap : : const_iterator i = shaders . begin ( ) ; i ! = shaders . end ( ) ; + + i ) {
ShaderMap : : value_type const & it = * i ;
GLuint shader = it . second ;
glAttachShader ( program , shader ) ;
}
// link
glLinkProgram ( program ) ;
check_link_status ( ) ;
}
// ***************
// status checking
void check_compile_status ( GLint shader , std : : string source = " " ) {
# if defined(DEBUG)
GLint status = GL_FALSE ;
glGetShaderiv ( shader , GL_COMPILE_STATUS , & status ) ;
if ( status = = GL_FALSE | | shader = = 0 ) {
std : : cerr < < " source: \n " < < source < < std : : endl ;
std : : cerr < < " shader reports: " < < std : : endl ;
show_shader_log ( shader ) ;
throw std : : runtime_error ( " Shader failed to compile " ) ;
}
# endif
}
void check_link_status ( ) {
# if defined(DEBUG)
GLint status ;
glGetProgramiv ( program , GL_LINK_STATUS , & status ) ;
if ( status = = GL_FALSE ) {
std : : cerr < < " linker reports: " < < std : : endl ;
show_program_log ( ) ;
throw std : : runtime_error ( " Shader failed to link " ) ;
}
# endif
}
void show_program_log ( std : : ostream & out = std : : cerr ) {
GLint infoLength = 0 ;
glGetProgramiv ( program , GL_INFO_LOG_LENGTH , & infoLength ) ;
char * infoBuffer = new GLchar [ infoLength + 10 ] ;
glGetProgramInfoLog ( program , infoLength + 10 , & infoLength , infoBuffer ) ;
out < < infoBuffer < < std : : endl ;
delete [ ] infoBuffer ;
}
void show_shader_log ( GLuint shader , std : : ostream & out = std : : cerr ) {
GLint infoLength = 0 ;
glGetShaderiv ( shader , GL_INFO_LOG_LENGTH , & infoLength ) ;
char * infoBuffer = new GLchar [ infoLength + 10 ] ;
glGetShaderInfoLog ( shader , infoLength + 10 , & infoLength , infoBuffer ) ;
out < < infoBuffer < < std : : endl ;
delete [ ] infoBuffer ;
}
} ;
} // namespace J
# endif // SHADER_H