You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
6.6 KiB
229 lines
6.6 KiB
#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
|
|
|