// This file is part of Noggit3, licensed under GNU General Public License (version 3). #include #include #include #include #include #include #include #include #include #include #include #include #include namespace OpenGL { shader::shader (GLenum type, std::string const& source) try : _handle (gl.createShader (type)) { char const* source_ptr (source.data()); gl.shaderSource (_handle, 1, &source_ptr, nullptr); gl.compile_shader (_handle); } catch (...) { std::throw_with_nested (std::runtime_error ("error constructing shader '" + source + "'")); } shader::~shader() { gl.deleteShader (_handle); } std::string shader::src_from_qrc(std::string const& shader_alias) { QFile f(QString::fromStdString(":/shader/" + shader_alias)); if (!f.open(QFile::ReadOnly | QFile::Text)) { throw std::logic_error("Could not load " + shader_alias + " from the qrc file"); } QTextStream stream(&f); return stream.readAll().toStdString(); } std::string shader::src_from_qrc(std::string const& shader_alias, std::vector const& defines) { std::string src(src_from_qrc(shader_alias)); if (defines.empty()) { return src; } std::stringstream ss; ss << "\n"; for (auto const& def : defines) { ss << "#define " << def << "\n"; } std::regex regex("#version[ \t]+[0-9]+.*"); std::smatch match; if (!std::regex_search(src, match, regex)) { throw std::logic_error("shader " + shader_alias + " has no #version directive"); } // #version is always the first thing in the shader, insert defines after it src.insert(match.length() + match.position(), ss.str()); return src; } program::program (std::initializer_list shaders) : _handle (gl.createProgram()) { struct scoped_attach { scoped_attach (GLuint program, GLuint shader) : _program (program) , _shader (shader) { gl.attachShader (_program, _shader); } ~scoped_attach() { gl.detachShader (_program, _shader); } GLuint _program; GLuint _shader; }; std::list attachments; for (shader const& s : shaders) { attachments.emplace_back (*_handle, s._handle); } gl.link_program (*_handle); #ifdef VALIDATE_OPENGL_PROGRAMS gl.validate_program(*_handle); #endif } program::program (program&& other) : _handle (std::nullopt) { std::swap (_handle, other._handle); } program::~program() { if (_handle) { if (QOpenGLContext::currentContext() && ::gl.getCurrentContext() != nullptr) { gl.deleteProgram(*_handle); } /*else { GLuint id = *_handle; gl.scheduleCleanup([id]() { gl.deleteProgram(id); } ); }*/ } } GLuint program::uniform_location (std::string const& name) const { return gl.getUniformLocation (*_handle, name.c_str()); } GLuint program::uniform_block_location (std::string const& name) const { return gl.getUniformBlockIndex(*_handle, name.c_str()); } GLuint program::attrib_location (std::string const& name) const { return gl.getAttribLocation (*_handle, name.c_str()); } namespace Scoped { use_program::use_program (program const& p) : _program (p) { gl.getIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast (&_old)); gl.useProgram (*_program._handle); } use_program::~use_program() { for (auto const& array : _enabled_vertex_attrib_arrays) { GLint cur_vao = 0; gl.getIntegerv(GL_VERTEX_ARRAY_BINDING, &cur_vao); if(cur_vao != array || !cur_vao) continue; gl.disableVertexAttribArray (array); } gl.useProgram (_old); } void use_program::uniform (std::string const& name, GLint value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform1i (loc, value); } void use_program::uniform (GLint pos, GLint value) { gl.uniform1i (pos, value); } void use_program::uniform (std::string const& name, GLfloat value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform1f (loc, value); } void use_program::uniform (GLint pos, GLfloat value) { gl.uniform1f (pos, value); } void use_program::uniform (std::string const& name, bool value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform1i (loc, static_cast(value)); } void use_program::uniform (GLint pos, bool value) { gl.uniform1i (pos, static_cast(value)); } void use_program::uniform_cached(std::string const& name, GLint value) { GLuint loc = uniform_location (name); if (loc < 0) return; auto cache = _program.getUniformsIntCache(); auto it = cache->find(loc); if (it == cache->end() || it->second != value) { (*const_cast*>(cache))[loc] = value; gl.uniform1i(loc, value); } } void use_program::uniform_cached(std::string const& name, GLfloat value) { GLuint loc = uniform_location (name); if (loc < 0) return; auto cache = _program.getUniformsFloatCache(); auto it = cache->find(loc); if (it == cache->end() || !misc::float_equals(it->second, value)) { (*const_cast*>(cache))[loc] = value; gl.uniform1f(loc, value); } } void use_program::uniform_cached(std::string const& name, bool value) { GLuint loc = uniform_location (name); if (loc < 0) return; auto cache = _program.getUniformsBoolCache(); auto it = cache->find(loc); if (it == cache->end() || it->second != value) { (*const_cast*>(cache))[loc] = value; gl.uniform1i(loc, static_cast(value)); } } void use_program::bind_uniform_block(std::string const& name, unsigned target) { gl.uniformBlockBinding(_program._handle.value() , uniform_block_location(name), target); } void use_program::uniform (std::string const& name, std::vector const& value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform1iv (loc, static_cast(value.size()), value.data()); } void use_program::uniform (std::string const& name, int const* data, std::size_t size) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform1iv (loc, static_cast(size), data); } void use_program::uniform (std::string const& name, glm::vec3 const* data, std::size_t size) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform3fv (loc, static_cast(size), reinterpret_cast(data)); } void use_program::uniform (GLint pos, std::vector const& value) { gl.uniform1iv (pos, static_cast(value.size()), value.data()); } void use_program::uniform (std::string const& name, std::vector const& value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform3fv (loc, static_cast(value.size()), glm::value_ptr(value[0])); } void use_program::uniform(std::string const& name, std::array const& value) { GLuint loc = uniform_location(name); if (loc < 0) return; gl.uniform3fv(loc, static_cast(8), glm::value_ptr(value[0])); } void use_program::uniform(std::string const& name, std::vector const& value) { GLuint loc = uniform_location(name); if (loc < 0) return; gl.uniform4fv(loc, static_cast(value.size()), glm::value_ptr(value[0])); } void use_program::uniform_chunk_textures (std::string const& name, std::array, 4>, 256> const& value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform2iv (loc, 256 * 4, reinterpret_cast(value.data())); } void use_program::uniform (GLint pos, std::vector const& value) { gl.uniform3fv (pos, static_cast(value.size()),glm::value_ptr(value[0])); } void use_program::uniform(GLint pos, std::vector const& value) { gl.uniform4fv(pos, static_cast(value.size()), glm::value_ptr(value[0])); } void use_program::uniform (std::string const& name, glm::vec2 const& value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform2fv(loc, 1, glm::value_ptr(value)); } void use_program::uniform (GLint pos, glm::vec2 const& value) { gl.uniform2fv (pos, 1, glm::value_ptr(value)); } void use_program::uniform (std::string const& name, glm::vec3 const& value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform3fv (loc, 1, glm::value_ptr(value)); } void use_program::uniform (GLint pos, glm::vec3 const& value) { gl.uniform3fv (pos, 1, glm::value_ptr(value)); } void use_program::uniform (std::string const& name, glm::vec4 const& value) { GLuint loc = uniform_location (name); if (loc < 0) return; gl.uniform4fv (loc, 1, glm::value_ptr(value)); } void use_program::uniform (GLint pos, glm::vec4 const& value) { gl.uniform4fv (pos, 1, glm::value_ptr(value)); } void use_program::uniform(std::string const& name, glm::mat4x4 const& value) { GLuint loc = uniform_location(name); if (loc < 0) return; gl.uniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(value)); } void use_program::uniform(GLint pos, glm::mat4x4 const& value) { gl.uniformMatrix4fv(pos, 1, GL_FALSE, glm::value_ptr(value)); } void use_program::sampler (std::string const& name, GLenum texture_slot, texture* tex) { uniform (name, GLint (texture_slot - GL_TEXTURE0)); texture::set_active_texture (texture_slot - GL_TEXTURE0); tex->bind(); } void use_program::attrib (std::string const& name, std::vector const& data) { GLuint const location (attrib_location (name)); gl.enableVertexAttribArray (location); _enabled_vertex_attrib_arrays.emplace (location); gl.vertexAttribPointer (location, 1, GL_FLOAT, GL_FALSE, 0, data.data()); } void use_program::attrib (std::string const& name, std::vector const& data) { GLuint const location (attrib_location (name)); gl.enableVertexAttribArray (location); _enabled_vertex_attrib_arrays.emplace (location); gl.vertexAttribPointer (location, 2, GL_FLOAT, GL_FALSE, 0, data.data()); } void use_program::attrib (std::string const& name, std::vector const& data) { GLuint const location (attrib_location (name)); gl.enableVertexAttribArray (location); _enabled_vertex_attrib_arrays.emplace (location); gl.vertexAttribPointer (location, 3, GL_FLOAT, GL_FALSE, 0, data.data()); } void use_program::attrib (std::string const& name, glm::vec3 const* data) { GLuint const location (attrib_location (name)); gl.enableVertexAttribArray (location); _enabled_vertex_attrib_arrays.emplace (location); gl.vertexAttribPointer (location, 3, GL_FLOAT, GL_FALSE, 0, data); } void use_program::attrib (std::string const& name, glm::mat4x4 const* data, GLuint divisor) { GLuint const location (attrib_location (name)); glm::vec4 const* vec4_ptr = reinterpret_cast(data); for (GLuint i = 0; i < 4; ++i) { gl.enableVertexAttribArray (location + i); _enabled_vertex_attrib_arrays.emplace (location + i); gl.vertexAttribPointer (location + i, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4x4), vec4_ptr + i); gl.vertexAttribDivisor(location + i, divisor); } } void use_program::attrib (std::string const& name, GLsizei size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* data) { GLuint const location (attrib_location (name)); gl.enableVertexAttribArray (location); _enabled_vertex_attrib_arrays.emplace (location); gl.vertexAttribPointer (location, size, type, normalized, stride, data); } void use_program::attrib (std::string const& name, GLuint buffer, GLsizei size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* data) { GLuint const location (attrib_location (name)); gl.enableVertexAttribArray (location); _enabled_vertex_attrib_arrays.emplace (location); Scoped::buffer_binder const bind (buffer); gl.vertexAttribPointer (location, size, type, normalized, stride, data); } void use_program::attribi (std::string const& name, GLsizei size, GLenum type, GLsizei stride, const GLvoid* data) { GLuint const location (attrib_location (name)); gl.enableVertexAttribArray (location); _enabled_vertex_attrib_arrays.emplace (location); gl.vertexAttribIPointer (location, size, type, stride, data); } void use_program::attribi (std::string const& name, GLuint buffer, GLsizei size, GLenum type, GLsizei stride, const GLvoid* data) { GLuint const location (attrib_location (name)); gl.enableVertexAttribArray (location); _enabled_vertex_attrib_arrays.emplace (location); Scoped::buffer_binder const bind (buffer); gl.vertexAttribIPointer (location, size, type, stride, data); } void use_program::attrib_divisor(std::string const& name, GLuint divisor, GLsizei range) { GLuint const location (attrib_location (name)); for (GLuint i = 0; i < static_cast(range); ++i) { gl.vertexAttribDivisor(location + i, divisor); } } GLuint use_program::uniform_location (std::string const& name) { auto uniforms = _program.getUniforms(); auto it = uniforms->find(name); if (it != uniforms->end()) { return it->second; } GLuint loc = _program.uniform_location(name); if (loc == -1) { LogError << "uniform " + name + " does not exist in shader\n" << std::endl; } (*const_cast*>(uniforms))[name] = loc; return loc; } GLuint use_program::uniform_block_location (std::string const& name) { auto uniforms = _program.getUniforms(); auto it = uniforms->find(name); if (it != uniforms->end()) { return it->second; } GLuint loc = _program.uniform_block_location(name); if (loc == -1) { throw std::invalid_argument ("uniform block " + name + " does not exist in shader\n"); } (*const_cast*>(uniforms))[name] = loc; return loc; } GLuint use_program::attrib_location (std::string const& name) { auto attribs = _program.getAttributes(); auto it = attribs->find (name); if (it != attribs->end()) { return it->second; } else { GLuint loc = _program.attrib_location (name); if (loc == -1) { throw std::invalid_argument ("attribute " + name + " does not exist in shader\n"); } (*const_cast*>(attribs))[name] = loc; return loc; } } } }