// This is Jeff LaMarche's GLProgram OpenGL shader wrapper class from his OpenGL ES 2.0 book. // A description of this can be found at his page on the topic: // http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html #import "GLProgram.h" // START:typedefs #pragma mark Function Pointer Definitions typedef void (*GLInfoFunction)(GLuint program, GLenum pname, GLint* params); typedef void (*GLLogFunction) (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog); // END:typedefs #pragma mark - #pragma mark Private Extension Method Declaration // START:extension @interface GLProgram() - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type string:(NSString *)shaderString; - (NSString *)logForOpenGLObject:(GLuint)object infoCallback:(GLInfoFunction)infoFunc logFunc:(GLLogFunction)logFunc; @end // END:extension #pragma mark - @implementation GLProgram // START:init @synthesize initialized = _initialized; - (id)initWithVertexShaderString:(NSString *)vShaderString fragmentShaderString:(NSString *)fShaderString; { if ((self = [super init])) { _initialized = NO; attributes = [[NSMutableArray alloc] init]; uniforms = [[NSMutableArray alloc] init]; program = glCreateProgram(); if (![self compileShader:&vertShader type:GL_VERTEX_SHADER string:vShaderString]) NSLog(@"Failed to compile vertex shader"); // Create and compile fragment shader if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER string:fShaderString]) NSLog(@"Failed to compile fragment shader"); glAttachShader(program, vertShader); glAttachShader(program, fragShader); } return self; } - (id)initWithVertexShaderString:(NSString *)vShaderString fragmentShaderFilename:(NSString *)fShaderFilename; { NSString *fragShaderPathname = [[NSBundle mainBundle] pathForResource:fShaderFilename ofType:@"fsh"]; NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragShaderPathname encoding:NSUTF8StringEncoding error:nil]; if ((self = [self initWithVertexShaderString:vShaderString fragmentShaderString:fragmentShaderString])) { } return self; } - (id)initWithVertexShaderFilename:(NSString *)vShaderFilename fragmentShaderFilename:(NSString *)fShaderFilename; { NSString *vertShaderPathname = [[NSBundle mainBundle] pathForResource:vShaderFilename ofType:@"vsh"]; NSString *vertexShaderString = [NSString stringWithContentsOfFile:vertShaderPathname encoding:NSUTF8StringEncoding error:nil]; NSString *fragShaderPathname = [[NSBundle mainBundle] pathForResource:fShaderFilename ofType:@"fsh"]; NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragShaderPathname encoding:NSUTF8StringEncoding error:nil]; if ((self = [self initWithVertexShaderString:vertexShaderString fragmentShaderString:fragmentShaderString])) { } return self; } // END:init // START:compile - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type string:(NSString *)shaderString { // CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); GLint status; const GLchar *source; source = (GLchar *)[shaderString UTF8String]; if (!source) { NSLog(@"Failed to load vertex shader"); return NO; } *shader = glCreateShader(type); glShaderSource(*shader, 1, &source, NULL); glCompileShader(*shader); glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { GLint logLength; glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); if (logLength > 0) { GLchar *log = (GLchar *)malloc(logLength); glGetShaderInfoLog(*shader, logLength, &logLength, log); NSLog(@"Shader compile log:\n%s", log); free(log); } } // CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime); // NSLog(@"Compiled in %f ms", linkTime * 1000.0); return status == GL_TRUE; } // END:compile #pragma mark - // START:addattribute - (void)addAttribute:(NSString *)attributeName { if (![attributes containsObject:attributeName]) { [attributes addObject:attributeName]; glBindAttribLocation(program, (GLuint)[attributes indexOfObject:attributeName], [attributeName UTF8String]); } } // END:addattribute // START:indexmethods - (GLuint)attributeIndex:(NSString *)attributeName { return (GLuint)[attributes indexOfObject:attributeName]; } - (GLuint)uniformIndex:(NSString *)uniformName { return glGetUniformLocation(program, [uniformName UTF8String]); } // END:indexmethods #pragma mark - // START:link - (BOOL)link { // CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); GLint status; glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) return NO; if (vertShader) { glDeleteShader(vertShader); vertShader = 0; } if (fragShader) { glDeleteShader(fragShader); fragShader = 0; } self.initialized = YES; // CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime); // NSLog(@"Linked in %f ms", linkTime * 1000.0); return YES; } // END:link // START:use - (void)use { glUseProgram(program); } // END:use #pragma mark - // START:privatelog - (NSString *)logForOpenGLObject:(GLuint)object infoCallback:(GLInfoFunction)infoFunc logFunc:(GLLogFunction)logFunc { GLint logLength = 0, charsWritten = 0; infoFunc(object, GL_INFO_LOG_LENGTH, &logLength); if (logLength < 1) return nil; char *logBytes = malloc(logLength); logFunc(object, logLength, &charsWritten, logBytes); NSString *log = [[NSString alloc] initWithBytes:logBytes length:logLength encoding:NSUTF8StringEncoding]; free(logBytes); return log; } // END:privatelog // START:log - (NSString *)vertexShaderLog { return [self logForOpenGLObject:vertShader infoCallback:(GLInfoFunction)&glGetProgramiv logFunc:(GLLogFunction)&glGetProgramInfoLog]; } - (NSString *)fragmentShaderLog { return [self logForOpenGLObject:fragShader infoCallback:(GLInfoFunction)&glGetProgramiv logFunc:(GLLogFunction)&glGetProgramInfoLog]; } - (NSString *)programLog { return [self logForOpenGLObject:program infoCallback:(GLInfoFunction)&glGetProgramiv logFunc:(GLLogFunction)&glGetProgramInfoLog]; } // END:log - (void)validate; { GLint logLength; glValidateProgram(program); glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); if (logLength > 0) { GLchar *log = (GLchar *)malloc(logLength); glGetProgramInfoLog(program, logLength, &logLength, log); NSLog(@"Program validate log:\n%s", log); free(log); } } #pragma mark - // START:dealloc - (void)dealloc { if (vertShader) glDeleteShader(vertShader); if (fragShader) glDeleteShader(fragShader); if (program) glDeleteProgram(program); } // END:dealloc @end