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.
311 lines
11 KiB
311 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 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
|
|
// NOTE: in ES2 there are 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
|
|
// 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:
|
|
|
|
// *******************
|
|
// 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(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
|
|
|