#ifndef GLSLPROGRAM_H #define GLSLPROGRAM_H #include "glslprogram.h" #include void GLSLProgram::Create( char *vfile, char *gfile, char *ffile ) { CanDoFragmentShader = IsExtensionSupported( "GL_ARB_fragment_shader" ); CanDoGeometryShader = IsExtensionSupported( "GL_EXT_geometry_shader4" ); CanDoVertexShader = IsExtensionSupported( "GL_ARB_vertex_shader" ); InputTopology = GL_TRIANGLES; OutputTopology = GL_TRIANGLE_STRIP; Vshader = Gshader = Fshader = 0; Program = 0; AttributeLocs.clear(); UniformLocs.clear(); Verbose = false; this->Program = glCreateProgram(); CheckGlErrors( "glCreateProgram" ); if( vfile != NULL && vfile[0] != '\0' ) { if( ! CanDoVertexShader ) { fprintf( stderr, "Warning: this system cannot handle vertex shaders\n" ); } this->Vshader = LoadVertexShader( vfile ); int status = CompileShader( this->Vshader ); if( status != 0 ) { if( this->Verbose ) fprintf( stderr, "Shader '%s' compiled.\n", vfile ); AttachShader( this->Vshader ); } else { fprintf( stderr, "Shader '%s' did not compile.\n", vfile ); } } if( gfile != NULL && gfile[0] != '\0' ) { if( ! CanDoGeometryShader ) { fprintf( stderr, "Warning: this system cannot handle geometry shaders\n" ); } this->Gshader = LoadGeometryShader( gfile ); int status = CompileShader( this->Gshader ); if( status != 0 ) { if( this->Verbose ) fprintf( stderr, "Shader '%s' compiled.\n", gfile ); AttachShader( Gshader ); } else { fprintf( stderr, "Shader '%s' did not compile.\n", gfile ); } } if( ffile != NULL && ffile[0] != '\0' ) { if( ! CanDoFragmentShader ) { fprintf( stderr, "Warning: this system cannot handle fragment shaders\n" ); } this->Fshader = LoadFragmentShader( ffile ); int status = CompileShader( this->Fshader ); if( status != 0 ) { if( this->Verbose ) fprintf( stderr, "Shader '%s' compiled.\n", ffile ); AttachShader( Fshader ); } else { fprintf( stderr, "Shader '%s' did not compile.\n", ffile ); } } LinkProgram(); }; // default constructor: GLSLProgram::GLSLProgram() { Create( NULL, NULL, NULL ); } // alternate constructor with a geometry shader: GLSLProgram::GLSLProgram( char *vfile, char *gfile, char *ffile ) { Create( vfile, gfile, ffile ); } // alternate constructor with no geometry shader: GLSLProgram::GLSLProgram( char *vfile, char *ffile ) { Create( vfile, NULL, ffile ); } GLuint GLSLProgram::LoadVertexShader( char *vfile ) { GLuint shader = glCreateShader( GL_VERTEX_SHADER ); if( LoadShader( vfile, shader ) != 0 ) { if( this->Verbose ) fprintf( stderr, "Vertex shader '%s' loaded.\n", vfile ); return shader; } else { fprintf( stderr, "Vertex shader '%s' failed to load.\n", vfile ); glDeleteShader( shader ); return 0; } }; GLuint GLSLProgram::LoadGeometryShader( char *gfile ) { GLuint shader = glCreateShader( GL_GEOMETRY_SHADER_EXT ); if( LoadShader( gfile, shader ) != 0 ) { if( this->Verbose ) fprintf( stderr, "Geometry shader '%s' loaded.\n", gfile ); return shader; } else { fprintf( stderr, "Geometry shader '%s' failed to load.\n", gfile ); glDeleteShader( shader ); return 0; } }; GLuint GLSLProgram::LoadFragmentShader( char *ffile ) { GLuint shader = glCreateShader( GL_FRAGMENT_SHADER ); if( LoadShader( ffile, shader ) != 0 ) { if( this->Verbose ) fprintf( stderr, "Fragment shader '%s' loaded.\n", ffile ); return shader; } else { fprintf( stderr, "Fragment shader '%s' failed to load.\n", ffile ); glDeleteShader( shader ); return 0; } }; int GLSLProgram::LoadShader( const char* fname, GLuint shader ) { FILE * in = fopen( fname, "rb" ); if( in == NULL ) { fprintf( stderr, "Cannot open shader file '%s'\n", fname ); return 0; } fseek( in, 0, SEEK_END ); int length = ftell( in ); fseek( in, 0, SEEK_SET ); // rewind GLubyte * buf = new GLubyte[length+1]; fread( buf, sizeof(GLubyte), length, in ); buf[length] = '\0'; fclose( in ) ; // Now, tell GL about the source! glShaderSource( shader, 1, (const char**)&buf, NULL ); delete [] buf; CheckGlErrors( "LoadShader:ShaderSource" ); return 1; }; int GLSLProgram::CompileShader( GLuint shader ) { glCompileShader( shader ); GLint infoLogLen; GLint compileStatus; CheckGlErrors( "CompileShader:" ); glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); if( compileStatus == 0 ) { fprintf( stderr, "Failed to compile shader.\n" ); glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &infoLogLen ); if( infoLogLen > 0) { GLchar *infoLog = new GLchar[infoLogLen+1]; glGetShaderInfoLog( shader, infoLogLen, NULL, infoLog); infoLog[infoLogLen] = '\0'; FILE * file = fopen( "glsllog.txt", "w"); fprintf( file, "\n%s\n", infoLog ); fprintf( stderr, "\n%s\n", infoLog ); fclose( file ); delete [] infoLog; } glDeleteShader( shader ); return 0; } CheckGlErrors("LoadShader:Compile 2"); return 1; }; void GLSLProgram::AttachShader( GLuint shader ) { glAttachShader( this->Program, shader ); }; int GLSLProgram::LinkProgram() { if( Gshader != 0 ) { glProgramParameteriEXT( Program, GL_GEOMETRY_INPUT_TYPE_EXT, InputTopology ); glProgramParameteriEXT( Program, GL_GEOMETRY_OUTPUT_TYPE_EXT, OutputTopology ); glProgramParameteriEXT( Program, GL_GEOMETRY_VERTICES_OUT_EXT, 1024 ); } glLinkProgram( Program ); CheckGlErrors("LoadShader:Link 1"); GLchar* infoLog; GLint infoLogLen; GLint linkStatus; glGetProgramiv( this->Program, GL_LINK_STATUS, &linkStatus ); CheckGlErrors("LoadShader:Link 2"); if( linkStatus == 0 ) { glGetProgramiv( this->Program, GL_INFO_LOG_LENGTH, &infoLogLen ); fprintf( stderr, "Failed to link program -- Info Log Length = %d\n", infoLogLen ); if( infoLogLen > 0 ) { infoLog = new GLchar[infoLogLen+1]; glGetProgramInfoLog( this->Program, infoLogLen, NULL, infoLog ); infoLog[infoLogLen] = '\0'; fprintf( stderr, "Info Log:\n%s\n", infoLog ); delete [] infoLog; } glDeleteProgram( Program ); glDeleteShader( Vshader ); glDeleteShader( Gshader ); glDeleteShader( Fshader ); return 0; } return 1; }; void GLSLProgram::SetVerbose( bool v ) { Verbose = v; }; void GLSLProgram::Use() { Use( this->Program ); }; void GLSLProgram::Use( int p ) { if( p != CurrentProgram ) { glUseProgram( p ); CurrentProgram = p; } }; int GLSLProgram::GetAttributeLocation( char *name ) { std::map::iterator pos; pos = AttributeLocs.find( name ); if( pos == AttributeLocs.end() ) { AttributeLocs[name] = glGetAttribLocation( this->Program, name ); } return AttributeLocs[name]; }; void GLSLProgram::SetAttribute( char* name, int val ) { int loc; if( ( loc = GetAttributeLocation( name ) ) >= 0 ) { this->Use(); glUniform1i( loc, val ); } }; void GLSLProgram::SetAttribute( char* name, float val ) { int loc; if( ( loc = GetAttributeLocation( name ) ) >= 0 ) { this->Use(); glUniform1f( loc, val ); } }; int GLSLProgram::GetUniformLocation( char *name ) { std::map::iterator pos; pos = UniformLocs.find( name ); if( pos == UniformLocs.end() ) { UniformLocs[name] = glGetUniformLocation( this->Program, name ); } return UniformLocs[name]; }; void GLSLProgram::SetUniform( char* name, int val ) { int loc; if( ( loc = GetUniformLocation( name ) ) >= 0 ) { this->Use(); glUniform1i( loc, val ); } }; void GLSLProgram::SetUniform( char* name, float val ) { int loc; if( ( loc = GetUniformLocation( name ) ) >= 0 ) { this->Use(); glUniform1f( loc, val ); } }; void GLSLProgram::SetInputTopology( GLenum t ) { if( t != GL_POINTS && t != GL_LINES && t != GL_LINES_ADJACENCY_EXT && t != GL_TRIANGLES && t != GL_TRIANGLES_ADJACENCY_EXT ) { fprintf( stderr, "Warning: You have not specified a supported Input Topology\n" ); } InputTopology = t; } void GLSLProgram::SetOutputTopology( GLenum t ) { if( t != GL_POINTS && t != GL_LINE_STRIP && t != GL_TRIANGLE_STRIP ) { fprintf( stderr, "Warning: You have not specified a supported Onput Topology\n" ); } OutputTopology = t; } bool GLSLProgram::IsExtensionSupported( const char *extension ) { // see if the extension is bogus: if( extension == NULL || extension[0] == '\0' ) return false; GLubyte *where = (GLubyte *) strchr( extension, ' ' ); if( where != 0 ) return false; // get the full list of extensions: const GLubyte *extensions = glGetString( GL_EXTENSIONS ); for( const GLubyte *start = extensions; ; ) { where = (GLubyte *) strstr( (const char *) start, extension ); if( where == 0 ) return false; GLubyte *terminator = where + strlen(extension); if( where == start || *(where - 1) == '\n' || *(where - 1) == ' ' ) if( *terminator == ' ' || *terminator == '\n' || *terminator == '\0' ) return true; start = terminator; } return false; } int GLSLProgram::CurrentProgram = 0; #endif #ifndef CHECK_GL_ERRORS #define CHECK_GL_ERRORS void CheckGlErrors( const char* caller ) { unsigned int gle = glGetError(); if( gle != GL_NO_ERROR ) { fprintf( stderr, "GL Error discovered from caller %s: ", caller ); switch (gle) { case GL_INVALID_ENUM: fprintf( stderr, "Invalid enum.\n" ); break; case GL_INVALID_VALUE: fprintf( stderr, "Invalid value.\n" ); break; case GL_INVALID_OPERATION: fprintf( stderr, "Invalid Operation.\n" ); break; case GL_STACK_OVERFLOW: fprintf( stderr, "Stack overflow.\n" ); break; case GL_STACK_UNDERFLOW: fprintf(stderr, "Stack underflow.\n" ); break; case GL_OUT_OF_MEMORY: fprintf( stderr, "Out of memory.\n" ); break; } return; } } #endif