/************************************************************************************ Authors : Bradley Austin Davis 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 #include #include #include #include #include #include #include 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 ShaderPtr; namespace Attribute { enum { Position = 0, TexCoord0 = 1, Normal = 2, Color = 3, TexCoord1 = 4, }; } class Program { typedef std::map Map; typedef std::set 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 & 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 & 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 ProgramPtr; } // gl