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.
 
 
 
 

300 lines
9.4 KiB

//
// shader.h
// J
//
// Created by Joshua Moerman on 7/01/11.
// Copyright 2011 Vadovas. All rights reserved.
//
#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: do glValidateProgram, like in the OpenGL template (see bottom)
// TODO: add error checking at set_uniforms?
// TODO: use an uniform map? (benchmark first!)
// TODO: allow arrays is uniforms (now all counts are 1)
// TODO: matrix attributes are not handled well (they take up 4 indices in the attributes)
// 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.
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
}
end();
glDeleteProgram(program);
}
// *****
// usage
void begin() {
glUseProgram(program);
}
void end() {
glUseProgram(0);
}
// *****************
// attribute setters
void set_attribute(const std::string& name, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr){
const GLuint index = attributes[name];
glVertexAttribPointer(index, size, type, normalized, stride, ptr);
glEnableVertexAttribArray(index);
}
// ***************
// uniform setters
// float vectors (multiple values)
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);
}
// integer vectors (multiple values)
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);
}
// Float vectors (array)
void set_uniform(char const* name, const GLfloat (&value)[4]) const {
glUniform4fv(glGetUniformLocation(program, name), 1, value);
}
void set_uniform(char const* name, const GLfloat (&value)[3]) const {
glUniform3fv(glGetUniformLocation(program, name), 1, value);
}
void set_uniform(char const* name, const GLfloat (&value)[2]) const {
glUniform2fv(glGetUniformLocation(program, name), 1, value);
}
void set_uniform(char const* name, const GLfloat (&value)[1]) const {
glUniform1fv(glGetUniformLocation(program, name), 1, value);
}
// Integer vectors (array)
void set_uniform(char const* name, const GLint (&value)[4]) const {
glUniform4iv(glGetUniformLocation(program, name), 1, value);
}
void set_uniform(char const* name, const GLint (&value)[3]) const {
glUniform3iv(glGetUniformLocation(program, name), 1, value);
}
void set_uniform(char const* name, const GLint (&value)[2]) const {
glUniform2iv(glGetUniformLocation(program, name), 1, value);
}
void set_uniform(char const* name, const GLint (&value)[1]) const {
glUniform1iv(glGetUniformLocation(program, name), 1, value);
}
// matrices (in ES2 only square matrices)
void set_uniform(char const* name, const GLfloat (&value)[16], GLboolean trans) const {
glUniformMatrix4fv(glGetUniformLocation(program, name), 1, trans, value);
}
void set_uniform(char const* name, const std::array<GLfloat, 16> & value, GLboolean trans) const {
glUniformMatrix4fv(glGetUniformLocation(program, name), 1, trans, &value[0]);
}
void set_uniform(char const* name, const GLfloat (&value)[9], GLboolean trans) const {
glUniformMatrix3fv(glGetUniformLocation(program, name), 1, trans, value);
}
void set_uniform(char const* name, const GLfloat (&value)[4], GLboolean trans) const {
glUniformMatrix2fv(glGetUniformLocation(program, name), 1, trans, value);
}
// textures (see todo above)
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:
// *******************
// compiling / linking
void compile_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);
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(GLint 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