void shader::_destroy() { if(m_program_id) { glDeleteProgram(m_program_id); m_program_id = 0; check_and_log_error("shader::_destroy - destroying program."); } if(m_vertex_id) { glDeleteShader(m_vertex_id); m_vertex_id = 0; check_and_log_error("shader::_destroy - destroying vertex shader."); } if(m_geometry_id) { glDeleteShader(m_geometry_id); m_geometry_id = 0; check_and_log_error("shader::_destroy - destroying geometry shader."); } if(m_fragment_id) { glDeleteShader(m_fragment_id); m_fragment_id = 0; check_and_log_error("shader::_destroy - destroying fragment shader."); } }
void ShaderProgram::set_uniform(const std::string& name, const kmVec4* vec) { GLint loc = get_uniform_loc(name); if(loc >= 0) { glUniform4fv(get_uniform_loc(name), 1, (GLfloat*) vec); check_and_log_error(__FILE__, __LINE__); } }
void index_buffer::bind() { if(is_valid()) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); check_and_log_error("index_buffer::bind - failed binding buffer"); } }
void ShaderProgram::set_uniform(const std::string& name, const kmMat3* matrix) { GLint loc = get_uniform_loc(name); if(loc >= 0) { float mat[9]; unsigned char i = 9; while(i--) { mat[i] = (float) matrix->mat[i]; } glUniformMatrix3fv(get_uniform_loc(name), 1, false, (GLfloat*)mat); check_and_log_error(__FILE__, __LINE__); } }
void vertex_buffer::_destroy() { if(m_vertex_buffer) { glDeleteBuffers(1, &m_vertex_buffer); m_vertex_buffer = 0; check_and_log_error("vertex_buffer::_destroy - destroying buffer."); } }
void vertex_buffer::bind(const vertex_format &set_vert_fmt, const shader &set_shader) const { if(!is_valid()) { log_error(GL_NO_ERROR, "vertex_buffer::bind - buffer is not valid."); return; } glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); check_and_log_error("vertex_buffer::bind - binding buffer."); // Vertex Format { for(std::size_t i = 0; i < set_vert_fmt.get_number_of_elements(); ++i) { const GLint NOT_USED = -1; attribute attrib = set_vert_fmt.get_attribute(i); GLint index = glGetAttribLocation(set_shader.get_program_gl_id(), attrib.name.c_str()); if(index != NOT_USED) { glEnableVertexAttribArray(index); glVertexAttribPointer(index, static_cast<GLint>(attrib.size), attrib.type, GL_FALSE, set_vert_fmt.get_stride(), (void*)attrib.pointer); check_and_log_error("vertex_buffer::bind - applying vertex format '" + attrib.name + "'."); } } } check_and_log_error("vertex_buffer::bind - applying vertex buffer."); }
ShaderProgram::~ShaderProgram() { try { for(uint32_t i = 0; i < ShaderType::SHADER_TYPE_MAX; ++i) { if(shader_ids_[i] != 0) { glDeleteShader(shader_ids_[i]); } } if(program_id_) { glDeleteProgram(program_id_); } check_and_log_error(__FILE__, __LINE__); } catch (...) { } }
bool index_buffer::load_index_buffer(const std::vector<uint32_t> &index) { if(m_index_buffer) { _destroy(); } m_number_of_indices = static_cast<uint32_t>(index.size()); glGenBuffers(1, &m_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), &index[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); check_and_log_error("index_buffer::load_index_buffer - loading buffer."); return (is_valid() ? true : false); }
void ShaderProgram::relink() { glLinkProgram(program_id_); check_and_log_error(__FILE__, __LINE__); GLint linked = 0; glGetProgramiv(program_id_, GL_LINK_STATUS, &linked); if(!linked) { GLint length; glGetProgramiv(program_id_, GL_INFO_LOG_LENGTH, &length); std::vector<char> log; log.resize(length); glGetProgramInfoLog(program_id_, length, NULL, &log[0]); L_ERROR(std::string(log.begin(), log.end())); } assert(linked); }
bool vertex_buffer::load_vertex_buffer(const std::vector<float> &vertex_data, const bool is_dynamic) { if(is_valid()) { log_error(GL_NO_ERROR, "vertex_buffer::load_shader - a vertex_buffer already exists, destroying old one."); _destroy(); } glGenBuffers(1, &m_vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); glBufferData(GL_ARRAY_BUFFER, vertex_data.size() * sizeof(float), vertex_data.data(), is_dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); if(is_valid()) { m_buffer_size = static_cast<GLuint>(vertex_data.size()); m_number_entries = vertex_data.size(); return true; } check_and_log_error("vertex_buffer::load_vertex_buffer - loading vertex buffer."); return false; }
void shader::bind() const { if(!is_valid()) { log_error(GL_NO_ERROR, "vertex_buffer::bind - shader program is not valid."); } glUseProgram(m_program_id); check_and_log_error("shader::bind - failed to use program."); // Constants { for(const auto &upload : m_pending_upload) { const constant_type type = (constant_type)std::get<UPLOAD_TYPE>(upload); const uint32_t index = std::get<UPLOAD_INDEX>(upload); const uint32_t number_of_elements = std::get<UPLOAD_NUM_ELEMENTS>(upload); const void *data = &(m_pending_upload_data.at(std::get<UPLOAD_START_INDEX>(upload))); switch(type) { case(constant_type::FLOAT): glUniform1fv(index, number_of_elements, (GLfloat*)data); break; case(constant_type::VEC2): glUniform2fv(index, number_of_elements, (GLfloat*)data); break; case(constant_type::VEC3): glUniform3fv(index, number_of_elements, (GLfloat*)data); break; case(constant_type::VEC4): glUniform4fv(index, number_of_elements, (GLfloat*)data); break; case(constant_type::INT): glUniform1iv(index, number_of_elements, (GLint*)data); break; case(constant_type::IVEC2): glUniform2iv(index, number_of_elements, (GLint*)data); break; case(constant_type::IVEC3): glUniform3iv(index, number_of_elements, (GLint*)data); break; case(constant_type::IVEC4): glUniform4iv(index, number_of_elements, (GLint*)data); break; case(constant_type::BOOL): glUniform1iv(index, number_of_elements, (GLint*)data); break; case(constant_type::BVEC2): glUniform2iv(index, number_of_elements, (GLint*)data); break; case(constant_type::BVEC3): glUniform3iv(index, number_of_elements, (GLint*)data); break; case(constant_type::BVEC4): glUniform4iv(index, number_of_elements, (GLint*)data); break; case(constant_type::MAT2): glUniformMatrix2fv(index, number_of_elements, GL_FALSE, (GLfloat*)data); break; case(constant_type::MAT3): glUniformMatrix3fv(index, number_of_elements, GL_FALSE, (GLfloat*)data); break; case(constant_type::MAT4): glUniformMatrix4fv(index, number_of_elements, GL_FALSE, (GLfloat*)data); break; default: assert(false); // Why did you get here? }; } m_pending_upload.clear(); m_pending_upload_data.clear(); } // Constants // Textures { const std::size_t number_of_textures = std::min<std::size_t>(m_pending_texture_data.size(), static_cast<std::size_t>(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)); for(std::size_t i = 0; i < number_of_textures; ++i) { const auto &tex = m_pending_texture_data.at(i); glActiveTexture(GL_TEXTURE0 + tex.index); check_and_log_error("shader::bind - applying active texture."); glBindTexture(tex.target, tex.texture_id); check_and_log_error("shader::bind - binding texture."); glTexParameteri(tex.target, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(tex.target, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(tex.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(tex.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); check_and_log_error("shader::bind - setting texParaeteri."); } m_pending_texture_data.clear(); } }
bool shader::load_shader(const shader_code &code) { if(is_valid()) { log_error(GL_NO_ERROR, "shader::load_shader - a shader program already exists, destroying old one."); _destroy(); } auto compile_shader = [](const std::string &shader_code, const GLenum shader_type, const std::string &shader_name) -> GLuint { if(shader_code.empty()) { return 0; } GLuint shader_id = glCreateShader(shader_type); const char * vert_source = shader_code.c_str(); glShaderSource(shader_id, 1, &vert_source, NULL); glCompileShader(shader_id); // Log GLint log_length; glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length); std::vector<GLchar> log; log.resize(log_length); glGetShaderInfoLog(shader_id, log_length, 0, &log[0]); if(log_length > 1) { std::string log_str; log_str.reserve(log.size()); log_str.append(&log[0]); // Did it compile GLint is_compiled; glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled); auto find_line = [](const std::string &log, const std::string &shader, const std::string &log_tag) -> std::string { // This needs to work much better. return ""; }; if(is_compiled == GL_FALSE) { log_error(GL_NO_ERROR, "shader::load_shader - errors compiling shader."); log_error(GL_NO_ERROR, log_str); return 0; } else { log_error(GL_NO_ERROR, "shader::load_shader - warnings compiling shader."); log_error(GL_NO_ERROR, log_str); } } return shader_id; }; m_vertex_id = compile_shader(code.vs_code, GL_VERTEX_SHADER, "vertex"); m_geometry_id = compile_shader(code.gs_code, GL_GEOMETRY_SHADER, "geometry"); m_fragment_id = compile_shader(code.ps_code, GL_FRAGMENT_SHADER, "fragment"); // Link the shader if(m_vertex_id && m_fragment_id) { m_program_id = glCreateProgram(); glAttachShader(m_program_id, m_vertex_id); if(m_geometry_id) { glAttachShader(m_program_id, m_geometry_id); } glAttachShader(m_program_id, m_fragment_id); glLinkProgram(m_program_id); // Check the log GLint log_length; glGetProgramiv(m_program_id, GL_INFO_LOG_LENGTH, &log_length); std::vector<GLchar> log; log.resize(log_length); glGetProgramInfoLog(m_program_id, log_length, 0, &log[0]); // Did it link GLint is_linked; glGetProgramiv(m_program_id, GL_LINK_STATUS, &is_linked); if(log_length > 1) { std::string log_str; log_str.reserve(log.size()); log_str.append(&log[0]); if(is_linked == GL_FALSE) { log_error(GL_NO_ERROR, "shader::load_shader - errors linking program."); log_error(GL_NO_ERROR, log_str); } else { log_error(GL_NO_ERROR, "shader::load_shader - warnings linking program."); log_error(GL_NO_ERROR, log_str); } } // Give up there if(is_linked == GL_FALSE) { m_program_id = 0; return false; } } // If linked else { //return false; } // Get uniforms { glUseProgram(m_program_id); check_and_log_error("shader::load_shader - failed to use program."); // Get uniforms. GLint uniform_count, uniform_length; glGetProgramiv(m_program_id, GL_ACTIVE_UNIFORMS, &uniform_count); glGetProgramiv(m_program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_length); m_samplers.reserve(uniform_count); m_constants.reserve(uniform_count); for(GLint i = 0; i < uniform_count; ++i) { GLenum gl_type = 0; GLint length = 0; GLint size = 0; std::vector<GLchar> uni_name; uni_name.reserve(uniform_length); glGetActiveUniform(m_program_id, i, uniform_length, &length, &size, &gl_type, uni_name.data()); const std::string uniform_name(uni_name.data()); // Is sampler? if((gl_type >= GL_SAMPLER_1D) && (gl_type <= GL_SAMPLER_2D_RECT_SHADOW)) { const GLint location = glGetUniformLocation(m_program_id, uniform_name.c_str()); check_and_log_error("shader::load_shader - getting uniform location."); glUniform1i(location, m_samplers.size()); m_samplers.emplace_back(sampler{uniform_name, static_cast<uint32_t>(m_samplers.size())}); } // Then uniform else { const std::string prefix = "gl_"; if(uniform_name.compare(0, prefix.length(), prefix) != 0) { const GLint index = glGetUniformLocation(m_program_id, uniform_name.c_str()); const auto type = get_gl_constant_type(gl_type); m_constants.emplace_back(constant{uniform_name, static_cast<uint32_t>(index), static_cast<int32_t>(type), static_cast<int32_t>(size)}); } } } m_samplers.shrink_to_fit(); m_constants.shrink_to_fit(); m_pending_upload_data.reserve(m_constants.size() * (4 * sizeof(float))); // Huristic to resever space for data. constants * vec4 m_pending_texture_data.reserve(m_constants.size()); m_pending_upload.reserve(m_constants.size()); } // getting uniforms and samplers glUseProgram(0); return true; }
void ShaderProgram::add_and_compile(ShaderType type, const std::string& source) { check_and_log_error(__FILE__, __LINE__); if(program_id_ == 0) { program_id_ = glCreateProgram(); check_and_log_error(__FILE__, __LINE__); } if(shader_ids_[type] != 0) { glDeleteShader(shader_ids_[type]); shader_ids_[type] = 0; check_and_log_error(__FILE__, __LINE__); } GLuint shader_type; switch(type) { case ShaderType::SHADER_TYPE_VERTEX: { L_DEBUG("Adding vertex shader"); shader_type = GL_VERTEX_SHADER; } break; case ShaderType::SHADER_TYPE_FRAGMENT: { L_DEBUG("Adding fragment shader"); shader_type = GL_FRAGMENT_SHADER; } break; default: throw std::logic_error("Invalid shader type"); } check_and_log_error(__FILE__, __LINE__); GLuint shader = glCreateShader(shader_type); check_and_log_error(__FILE__, __LINE__); shader_ids_[type] = shader; const char* c_str = source.c_str(); glShaderSource(shader, 1, &c_str, nullptr); check_and_log_error(__FILE__, __LINE__); glCompileShader(shader); check_and_log_error(__FILE__, __LINE__); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if(!compiled) { GLint length; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); std::vector<char> log; log.resize(length); glGetShaderInfoLog(shader, length, NULL, &log[0]); L_ERROR(std::string(log.begin(), log.end())); } assert(compiled); assert(program_id_); assert(shader); check_and_log_error(__FILE__, __LINE__); glAttachShader(program_id_, shader); check_and_log_error(__FILE__, __LINE__); relink(); }
void ShaderProgram::bind_attrib(uint32_t idx, const std::string& name) { glBindAttribLocation(program_id_, idx, name.c_str()); check_and_log_error(__FILE__, __LINE__); }
void ShaderProgram::activate() { glUseProgram(program_id_); check_and_log_error(__FILE__, __LINE__); }
void ShaderProgram::set_uniform(const std::string& name, const int32_t x) { glUniform1i(get_uniform_loc(name), x); check_and_log_error(__FILE__, __LINE__); }