#include #include #include #include #include #include #include namespace psemek::gfx { void program::uniform_proxy::operator = (bool b) { gl::Uniform1i(location_, b ? 1 : 0); } void program::uniform_proxy::operator = (int i) { gl::Uniform1i(location_, i); } void program::uniform_proxy::operator = (unsigned int u) { gl::Uniform1ui(location_, u); } void program::uniform_proxy::operator = (float f) { gl::Uniform1f(location_, f); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform1i(location_, v[0]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform2i(location_, v[0], v[1]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform3i(location_, v[0], v[1], v[2]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform4i(location_, v[0], v[1], v[2], v[3]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform1ui(location_, v[0]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform2ui(location_, v[0], v[1]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform3ui(location_, v[0], v[1], v[2]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform4ui(location_, v[0], v[1], v[2], v[3]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform1f(location_, v[0]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform2f(location_, v[0], v[1]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform3f(location_, v[0], v[1], v[2]); } void program::uniform_proxy::operator = (math::vector const & v) { gl::Uniform4f(location_, v[0], v[1], v[2], v[3]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform1i(location_, v[0]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform2i(location_, v[0], v[1]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform3i(location_, v[0], v[1], v[2]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform4i(location_, v[0], v[1], v[2], v[3]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform1ui(location_, v[0]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform2ui(location_, v[0], v[1]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform3ui(location_, v[0], v[1], v[2]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform4ui(location_, v[0], v[1], v[2], v[3]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform1f(location_, v[0]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform2f(location_, v[0], v[1]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform3f(location_, v[0], v[1], v[2]); } void program::uniform_proxy::operator = (math::point const & v) { gl::Uniform4f(location_, v[0], v[1], v[2], v[3]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix2fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix3x2fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix4x2fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix2x3fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix3fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix4x3fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix2x4fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix3x4fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { gl::UniformMatrix4fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::interval const & i) { gl::Uniform2i(location_, i.min, i.max); } void program::uniform_proxy::operator = (math::interval const & i) { gl::Uniform2ui(location_, i.min, i.max); } void program::uniform_proxy::operator = (math::interval const & i) { gl::Uniform2f(location_, i.min, i.max); } void program::uniform_proxy::operator = (math::quaternion const & q) { (*this) = q.coords; } static std::string annotated_source(std::string_view source) { std::ostringstream os; bool first = true; int line = 1; for (char c : source) { if (first) { os << line << ": "; first = false; } os.put(c); if (c == '\n') { ++line; first = true; } } return os.str(); } static void load_shader(GLuint shader, std::string_view source) { char const * vert_sources[1] { source.data() }; GLint vert_sources_len[1] { static_cast(source.size()) }; gl::ShaderSource(shader, 1, vert_sources, vert_sources_len); gl::CompileShader(shader); GLint status; gl::GetShaderiv(shader, gl::COMPILE_STATUS, &status); if (status != gl::TRUE) { GLint log_len; gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &log_len); std::unique_ptr log(new char [log_len]); gl::GetShaderInfoLog(shader, log_len, nullptr, log.get()); throw util::exception(util::to_string("Shader compilation failed: ", log.get(), "\nShader source: \n", annotated_source(source))); } } static void load_program(GLuint program, std::vector const & shaders) { for (auto s : shaders) gl::AttachShader(program, s); gl::LinkProgram(program); GLint status; gl::GetProgramiv(program, gl::LINK_STATUS, &status); if (status != gl::TRUE) { GLint log_len; gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &log_len); std::unique_ptr log(new char [log_len]); gl::GetProgramInfoLog(program, log_len, nullptr, log.get()); throw util::exception(util::to_string("Program link failed: ", log.get())); } for (auto s : shaders) gl::DetachShader(program, s); } static GLuint create_program(std::vector> const & sources) { std::vector shaders; [[maybe_unused]] util::at_scope_exit delete_shaders([&shaders]{ for (auto s : shaders) gl::DeleteShader(s); }); for (auto const & p : sources) { GLuint sh = gl::CreateShader(p.first); shaders.push_back(sh); load_shader(sh, p.second); } GLuint program = gl::CreateProgram(); try { load_program(program, shaders); } catch (...) { gl::DeleteProgram(program); throw; } return program; } program::program(std::string_view compute_source) { program_ = create_program({{gl::COMPUTE_SHADER, compute_source}}); } program::program(std::string_view vertex_source, std::string_view fragment_source) { program_ = create_program({{gl::VERTEX_SHADER, vertex_source}, {gl::FRAGMENT_SHADER, fragment_source}}); } program::program(std::string_view vertex_source, std::string_view geometry_source, std::string_view fragment_source) { program_ = create_program({{gl::VERTEX_SHADER, vertex_source}, {gl::GEOMETRY_SHADER, geometry_source}, {gl::FRAGMENT_SHADER, fragment_source}}); } program::program(program && other) : program_(other.program_) , uniforms_(std::move(other.uniforms_)) { other.program_ = 0; } program & program::operator = (program && other) { if (this == &other) return *this; reset(); program_ = other.program_; uniforms_ = std::move(other.uniforms_); other.program_ = 0; return *this; } program::~program() { reset(); } GLuint program::id() const { return program_; } void program::bind() const { gl::UseProgram(program_); } void program::reset() { gl::DeleteProgram(program_); program_ = 0; } GLint program::location(std::string_view name) const { auto it = uniforms_.find(name); if (it == uniforms_.end()) { std::string name_str(name); auto location = gl::GetUniformLocation(program_, name_str.data()); uniforms_[std::move(name_str)] = location; return location; } return it->second; } program::uniform_proxy program::operator[] (std::string_view name) const { return {location(name)}; } program::uniform_proxy program::operator[] (GLint location) const { return {location}; } GLuint program::uniform_block_index(std::string_view name) const { auto it = uniform_blocks_.find(name); if (it == uniform_blocks_.end()) { std::string name_str(name); auto index = gl::GetUniformBlockIndex(program_, name.data()); uniform_blocks_[std::move(name_str)] = index; return index; } return it->second; } void program::uniform_block_binding(std::string_view name, GLuint binding) const { uniform_block_binding(uniform_block_index(name), binding); } void program::uniform_block_binding(GLuint index, GLuint binding) const { if (index != gl::INVALID_INDEX) gl::UniformBlockBinding(program_, index, binding); } }