# ifndef SHADER_H
# define SHADER_H
# include <iostream>
# include <fstream>
# include <stdexcept>
# include <map>
# include <iterator>
# import <OpenGLES / ES2 / gl.h>
# import <OpenGLES / ES2 / glext.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!)
namespace J {
enum {
ATTRIB_VERTEX ,
ATTRIB_COLOR ,
NUM_ATTRIBUTES
} ;
class shader {
typedef std : : map < GLenum , GLuint > ShaderMap ;
GLuint program ;
ShaderMap shaders ;
public :
shader ( std : : string vertex_shader_filename , std : : string fragment_shader_filename ) :
program ( 0 ) , shaders ( ) {
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 " ) ;
read_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 " ) ;
read_shader ( fragment_shader , GL_FRAGMENT_SHADER ) ;
}
{ // TODO: think of a nice way to implement this
// Bind attribute locations.
// This needs to be done prior to linking.
glBindAttribLocation ( program , ATTRIB_VERTEX , " position " ) ;
glBindAttribLocation ( program , ATTRIB_COLOR , " color " ) ;
}
link_program ( ) ;
}
~ 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
}
glDeleteProgram ( program ) ;
end ( ) ;
}
void begin ( ) {
glUseProgram ( program ) ;
}
void end ( ) {
glUseProgram ( 0 ) ;
}
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 ) ;
}
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 ) ;
}
// TODO: add support for other matrices (not only 4x4), in ES2 only square matrices
void set_matrix ( char const * name , bool trans , const GLfloat * value ) const {
glUniformMatrix4fv ( glGetUniformLocation ( program , name ) , 1 , trans , value ) ;
}
// TODO: add support for other vectors (not only length 4)
void set_uniform ( char const * name , const GLfloat * value , GLsizei count = 1 ) const {
glUniform4fv ( glGetUniformLocation ( program , name ) , count , value ) ;
}
// TODO: add support for other vectors (not only length 4)
void set_uniform ( char const * name , const GLint * value , GLsizei count = 1 ) const {
glUniform4iv ( glGetUniformLocation ( program , name ) , count , value ) ;
}
void set_texture ( const char * name , GLenum target , GLuint tex , int textureLocation ) const {
glActiveTexture ( GL_TEXTURE0 + textureLocation ) ;
glBindTexture ( target , tex ) ;
set_uniform ( name , textureLocation ) ;
}
private :
// FIXME: this also compiles, bad name
void read_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 ) ;
# if defined(DEBUG)
// check compile status
GLint status = GL_FALSE ;
glGetShaderiv ( shader , GL_COMPILE_STATUS , & status ) ;
if ( status = = GL_FALSE | | shader = = 0 ) {
GLsizei infoLength = 0 ;
glGetShaderiv ( shader , GL_INFO_LOG_LENGTH , & infoLength ) ;
char * infoBuffer = new GLchar [ infoLength ] ;
glGetShaderInfoLog ( shader , infoLength , & infoLength , infoBuffer ) ;
std : : cerr < < " source: \n " < < buffer < < std : : endl ;
std : : cerr < < " shader reports: \n " < < infoBuffer < < std : : endl ;
delete [ ] infoBuffer ;
throw std : : runtime_error ( " Shader failed to compile " ) ;
}
# endif
// 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 ) ;
# if defined(DEBUG)
// check link status
GLint status ;
glGetProgramiv ( program , GL_LINK_STATUS , & status ) ;
if ( status = = GL_FALSE ) {
GLsizei infoLength = 0 ;
glGetProgramiv ( program , GL_INFO_LOG_LENGTH , & infoLength ) ;
char * infoBuffer = new GLchar [ infoLength ] ;
glGetProgramInfoLog ( program , infoLength , & infoLength , infoBuffer ) ;
std : : cerr < < " linker reports: \n " < < infoBuffer < < std : : endl ;
delete [ ] infoBuffer ;
throw std : : runtime_error ( " Shader failed to link " ) ;
}
# endif
}
/*
// Validate program before drawing. This is a good check, but only really necessary in a debug build.
- ( BOOL ) validateProgram : ( GLuint ) prog
{
GLint logLength , status ;
glValidateProgram ( prog ) ;
glGetProgramiv ( prog , GL_INFO_LOG_LENGTH , & logLength ) ;
if ( logLength > 0 )
{
GLchar * log = ( GLchar * ) malloc ( logLength ) ;
glGetProgramInfoLog ( prog , logLength , & logLength , log ) ;
NSLog ( @ " Program validate log: \n %s " , log ) ;
free ( log ) ;
}
glGetProgramiv ( prog , GL_VALIDATE_STATUS , & status ) ;
if ( status = = 0 )
return FALSE ;
return TRUE ;
}
*/
} ;
} // namespace J
# endif // SHADER_H