330 lines
8.3 KiB
C++
330 lines
8.3 KiB
C++
/************************************************************************************
|
|
|
|
Authors : Bradley Austin Davis <bdavis@saintandreas.org>
|
|
Copyright : Copyright Brad Davis. All Rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
************************************************************************************/
|
|
|
|
#pragma once
|
|
|
|
#ifndef GL_ZERO
|
|
#error "You must include the gl headers before including this file"
|
|
#endif
|
|
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
#include <string>
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <set>
|
|
#include <memory>
|
|
|
|
namespace gl {
|
|
|
|
struct Var {
|
|
std::string name;
|
|
GLint size;
|
|
GLenum type;
|
|
GLuint id;
|
|
GLint location;
|
|
|
|
// for map usage
|
|
Var()
|
|
: location(0), type(0), size(0), id(0) {
|
|
}
|
|
Var(bool uniform, GLuint program, GLuint id) {
|
|
this->id = id;
|
|
static GLchar MY_BUFFER[8192];
|
|
GLsizei bufSize = 8192;
|
|
if (uniform) {
|
|
glGetActiveUniform(program, id, bufSize, &bufSize, &size, &type,
|
|
MY_BUFFER);
|
|
} else {
|
|
glGetActiveAttrib(program, id, bufSize, &bufSize, &size, &type,
|
|
MY_BUFFER);
|
|
}
|
|
name = std::string(MY_BUFFER, bufSize);
|
|
if (uniform) {
|
|
location = glGetUniformLocation(program, name.c_str());
|
|
} else {
|
|
location = glGetAttribLocation(program, name.c_str());
|
|
}
|
|
}
|
|
};
|
|
|
|
class Shader {
|
|
friend class Program;
|
|
GLenum type;
|
|
GLuint shader;
|
|
std::string source;
|
|
|
|
public:
|
|
Shader(GLenum type, const std::string & source)
|
|
: type(type), shader(0), source(source) {
|
|
|
|
// Create the shader object
|
|
GLuint newShader = glCreateShader(type);
|
|
if (newShader == 0) {
|
|
throw std::runtime_error("Unable to create shader object");
|
|
}
|
|
|
|
{
|
|
static const char * c_str;
|
|
// std::string shaderSrc = Files::read(fileName);
|
|
// Load the shader source
|
|
glShaderSource(newShader, 1, &(c_str = source.c_str()), NULL);
|
|
}
|
|
|
|
// Compile the shader
|
|
glCompileShader(newShader);
|
|
|
|
// Check the compile status
|
|
GLint compiled;
|
|
glGetShaderiv(newShader, GL_COMPILE_STATUS, &compiled);
|
|
if (!compiled) {
|
|
std::string log = getLog(newShader);
|
|
throw shader_error(shader, log);
|
|
}
|
|
|
|
if (0 != shader) {
|
|
glDeleteShader(shader);
|
|
}
|
|
shader = newShader;
|
|
}
|
|
|
|
virtual ~Shader() {
|
|
glDeleteShader(shader);
|
|
}
|
|
|
|
static std::string getLog(GLuint shader) {
|
|
std::string log;
|
|
GLint infoLen = 0;
|
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
|
|
|
|
if (infoLen > 1) {
|
|
char* infoLog = new char[infoLen];
|
|
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
|
|
log = std::string(infoLog);
|
|
delete[] infoLog;
|
|
}
|
|
return log;
|
|
}
|
|
};
|
|
|
|
typedef std::shared_ptr<Shader> ShaderPtr;
|
|
|
|
namespace Attribute {
|
|
enum {
|
|
Position = 0,
|
|
TexCoord0 = 1,
|
|
Normal = 2,
|
|
Color = 3,
|
|
TexCoord1 = 4,
|
|
};
|
|
}
|
|
|
|
class Program {
|
|
typedef std::map<std::string, Var> Map;
|
|
typedef std::set<std::string> Set;
|
|
|
|
ShaderPtr vs;
|
|
ShaderPtr fs;
|
|
GLuint program;
|
|
Map uniforms;
|
|
Map attributes;
|
|
Set configuredUniforms;
|
|
public:
|
|
GLint getUniformLocation(const std::string & name) const {
|
|
return uniforms.count(name) ? uniforms.at(name).location : -1;
|
|
}
|
|
|
|
GLint getAttributeLocation(const std::string & name) const {
|
|
return attributes.count(name) ? attributes.at(name).location : -1;
|
|
}
|
|
|
|
public:
|
|
static std::string getLog(GLuint program) {
|
|
std::string log;
|
|
GLint infoLen = 0;
|
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
|
|
|
|
if (infoLen > 1) {
|
|
char* infoLog = new char[infoLen];
|
|
glGetProgramInfoLog(program, infoLen, NULL, infoLog);
|
|
log = std::string(infoLog);
|
|
delete[] infoLog;
|
|
}
|
|
return log;
|
|
}
|
|
|
|
public:
|
|
Program(const ShaderPtr & vs, const ShaderPtr & fs)
|
|
: vs(vs), fs(fs), program(0) {
|
|
if (0 != program) {
|
|
glDeleteProgram(program);
|
|
program = 0;
|
|
}
|
|
|
|
// Create the program object
|
|
program = glCreateProgram();
|
|
|
|
if (program == 0)
|
|
throw std::runtime_error("Failed to allocate gl program");
|
|
|
|
glAttachShader(program, vs->shader);
|
|
glAttachShader(program, fs->shader);
|
|
|
|
// Bind vPosition to attribute 0
|
|
glBindAttribLocation(program, 0, "vPosition");
|
|
|
|
// Link the newProgram
|
|
glLinkProgram(program);
|
|
|
|
// Check the link status
|
|
GLint linked;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
|
if (!linked) {
|
|
std::string log = getLog(program);
|
|
throw std::runtime_error(log);
|
|
}
|
|
|
|
int numVars;
|
|
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &numVars);
|
|
for (int i = 0; i < numVars; ++i) {
|
|
Var var(false, program, i);
|
|
// SAY("Found attribute %s at location %d", var.name.c_str(), var.location);
|
|
attributes[var.name] = var;
|
|
}
|
|
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numVars);
|
|
for (int i = 0; i < numVars; ++i) {
|
|
Var var(true, program, i);
|
|
//SAY("Found uniform %s at location %d", var.name.c_str(), var.location);
|
|
uniforms[var.name] = var;
|
|
}
|
|
}
|
|
|
|
virtual ~Program() {
|
|
glDeleteProgram(program);
|
|
}
|
|
|
|
void setUniform(const std::string & name, GLfloat a) {
|
|
configuredUniforms.insert(name);
|
|
GLint location = getUniformLocation(name);
|
|
glUniform1f(location, a);
|
|
}
|
|
|
|
void setUniform(const std::string & name, GLint a) {
|
|
configuredUniforms.insert(name);
|
|
GLint location = getUniformLocation(name);
|
|
glUniform1i(location, a);
|
|
}
|
|
|
|
void setUniform(const std::string & name, bool a) {
|
|
configuredUniforms.insert(name);
|
|
GLint location = getUniformLocation(name);
|
|
glUniform1i(location, a ? 1 : 0);
|
|
}
|
|
|
|
void setUniform(const std::string & name, const glm::vec2 & a) {
|
|
configuredUniforms.insert(name);
|
|
GLint location = getUniformLocation(name);
|
|
glUniform2f(location, a.x, a.y);
|
|
}
|
|
|
|
void setUniform(const std::string & name, const glm::vec3 & a) {
|
|
configuredUniforms.insert(name);
|
|
GLint location = getUniformLocation(name);
|
|
glUniform3f(location, a.x, a.y, a.z);
|
|
}
|
|
|
|
void setUniform(const std::string & name, const glm::vec4 & a) {
|
|
configuredUniforms.insert(name);
|
|
GLint location = getUniformLocation(name);
|
|
glUniform4f(location, a.x, a.y, a.z, a.w);
|
|
}
|
|
|
|
void setUniform(const std::string & name, const std::vector<glm::vec4> & a) {
|
|
configuredUniforms.insert(name);
|
|
GLint location = getUniformLocation(name);
|
|
glUniform4fv(location, (GLsizei) a.size(), &(a[0][0]));
|
|
}
|
|
|
|
void setUniform(const std::string & name, const glm::mat4 & a) {
|
|
configuredUniforms.insert(name);
|
|
GLint location = getUniformLocation(name);
|
|
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(a));
|
|
}
|
|
|
|
inline void setUniform1f(const std::string & name, GLfloat a) {
|
|
setUniform(name, a);
|
|
}
|
|
|
|
inline void setUniform1i(const std::string & name, GLint a) {
|
|
setUniform(name, a);
|
|
}
|
|
|
|
inline void setUniform2f(const std::string & name, const glm::vec2 & a) {
|
|
setUniform(name, a);
|
|
}
|
|
|
|
inline void setUniform4f(const std::string & name, const glm::vec4 & a) {
|
|
setUniform(name, a);
|
|
}
|
|
|
|
inline void setUniform3f(const std::string & name, const glm::vec3 & a) {
|
|
setUniform(name, a);
|
|
}
|
|
|
|
inline void setUniform4fv(const std::string & name, const std::vector<glm::vec4> & a) {
|
|
setUniform(name, a);
|
|
}
|
|
|
|
inline void setUniform4x4f(const std::string & name, const glm::mat4 & a) {
|
|
setUniform(name, a);
|
|
}
|
|
|
|
void checkConfigured() {
|
|
Set existingUniforms;
|
|
// Option #2
|
|
std::for_each(std::begin(uniforms), std::end(uniforms),
|
|
[&] (Map::const_reference element)
|
|
{
|
|
existingUniforms.insert(element.first);
|
|
});
|
|
|
|
std::for_each(std::begin(configuredUniforms),
|
|
std::end(configuredUniforms), [&] (Set::const_reference element)
|
|
{
|
|
existingUniforms.erase(element);
|
|
});
|
|
|
|
// assert(existingUniforms.size() == 0);
|
|
}
|
|
|
|
void use() {
|
|
glUseProgram(program);
|
|
}
|
|
|
|
static void clear() {
|
|
glUseProgram(0);
|
|
}
|
|
};
|
|
|
|
typedef std::shared_ptr<Program> ProgramPtr;
|
|
|
|
} // gl
|