Very small OpenGL wrapper (before moggle was there)
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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 
 
 
 

320 lines
11 KiB

//
// shader.h
// J
//
// Created by Joshua Moerman on 7/01/11.
// Copyright 2011 Vadovas. All rights reserved.
//
/*
This is a very basic shader class. The compilation and linking is done in the constructor, and deletion is don in the destructor. As a result, you can't copy a shader, use smart pointers instead. Because OpenGL ES2 uses attribute-indices at link-time, you have to specify your attributes in the constructor (this is a bit cumbersome).
There are a lot of uniform setters, for both values and array's, they are called set_uniform() for all types. For matrices a additional parameter (trans) can be given, but in ES2 this value has to be false. It's still a parameter to avoid ambiguity, because the overload works with c-style arrays (vector4 is not distinguishable from matrix2x2). A fix would be to introduce types like vector4 and matrix2x2 and such, but Astrant already has those, so I refrain to do that.
Using the setters should only be done between the begin() and end() functions.
*/
#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: add error checking at set_uniforms?
// TODO: use an uniform map? (benchmark first!)
// TODO: allow arrays in uniforms (now all counts are 1). To make this possible, first make vector and matrix structs. (But Astrant already has those)
// TODO: matrix attributes are not handled well (they take up 4 indices in the attributes instead of one)
// TODO: make the attribute-list nicer (it is really cumbersome now, with the vector)
// 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. There is no geometry_shader in ES2.
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. Also deleting a shader with name '0' is safe.
}
end();
glDeleteProgram(program);
}
// *****
// usage
void begin() {
glUseProgram(program);
}
void end() {
glUseProgram(0);
}
// *****************
// attribute setters
// NOTE: the normalized parameter is for integer types, if true they will be mapped to [0, 1], if false it stays in [0, 256] or so.
void set_attribute(const std::string& name, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) const {
auto it = attributes.find(name);
if(it == attributes.end()) throw std::runtime_error(name + " attribute could not be found");
glEnableVertexAttribArray(it->second);
glVertexAttribPointer(it->second, size, type, normalized, stride, ptr);
}
// ***************
// uniform setters
// float vectors (multiple values)
void set_uniform(char const* name, float x) const {
glUniform1f(get_uniform_location(name), x);
}
void set_uniform(char const* name, float x, float y) const {
glUniform2f(get_uniform_location(name), x, y);
}
void set_uniform(char const* name, float x, float y, float z) const {
glUniform3f(get_uniform_location(name), x, y, z);
}
void set_uniform(char const* name, float x, float y, float z, float w) const {
glUniform4f(get_uniform_location(name), x, y, z, w);
}
// integer vectors (multiple values)
void set_uniform(char const* name, int x) const {
glUniform1i(get_uniform_location(name), x);
}
void set_uniform(char const* name, int x, int y) const {
glUniform2i(get_uniform_location(name), x, y);
}
void set_uniform(char const* name, int x, int y, int z) const {
glUniform3i(get_uniform_location(name), x, y, z);
}
void set_uniform(char const* name, int x, int y, int z, int w) const {
glUniform4i(get_uniform_location(name), x, y, z, w);
}
// Float vectors (array)
void set_uniform(char const* name, const GLfloat (&value)[4]) const {
glUniform4fv(get_uniform_location(name), 1, value);
}
void set_uniform(char const* name, const GLfloat (&value)[3]) const {
glUniform3fv(get_uniform_location(name), 1, value);
}
void set_uniform(char const* name, const GLfloat (&value)[2]) const {
glUniform2fv(get_uniform_location(name), 1, value);
}
void set_uniform(char const* name, const GLfloat (&value)[1]) const {
glUniform1fv(get_uniform_location(name), 1, value);
}
// Integer vectors (array)
void set_uniform(char const* name, const GLint (&value)[4]) const {
glUniform4iv(get_uniform_location(name), 1, value);
}
void set_uniform(char const* name, const GLint (&value)[3]) const {
glUniform3iv(get_uniform_location(name), 1, value);
}
void set_uniform(char const* name, const GLint (&value)[2]) const {
glUniform2iv(get_uniform_location(name), 1, value);
}
void set_uniform(char const* name, const GLint (&value)[1]) const {
glUniform1iv(get_uniform_location(name), 1, value);
}
// matrices
// NOTE: in ES2 there are only square matrices
void set_uniform(char const* name, const GLfloat (&value)[16], GLboolean trans) const {
glUniformMatrix4fv(get_uniform_location(name), 1, trans, value);
}
void set_uniform(char const* name, const std::array<GLfloat, 16> & value, GLboolean trans) const {
glUniformMatrix4fv(get_uniform_location(name), 1, trans, &value[0]);
}
void set_uniform(char const* name, const GLfloat (&value)[9], GLboolean trans) const {
glUniformMatrix3fv(get_uniform_location(name), 1, trans, value);
}
void set_uniform(char const* name, const GLfloat (&value)[4], GLboolean trans) const {
glUniformMatrix2fv(get_uniform_location(name), 1, trans, value);
}
// textures
// NOTE: with a texture class this can be made prettier (also se the TODO in fbo.h)
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:
mutable std::map<std::string, GLint> uniforms;
GLint get_uniform_location(char const * name) const {
auto it = uniforms.find(name);
if(it != uniforms.end())
return it->second;
return uniforms[name] = glGetUniformLocation(program, name);
}
// *******************
// compiling / linking
void compile_shader(std::istream& file, GLenum type) {
// get the c-string, the shortest way (not fastest), from http://tinodidriksen.com/
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(GLuint 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