// Copyright 2021, 2022 Benjamin Barenblat // // 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 // // https://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. // Drawing to the framebuffer. #ifndef GLPLANET_SRC_GL_DRAW_H_ #define GLPLANET_SRC_GL_DRAW_H_ #include #include #include #include "src/gl/buffer.h" #include "src/gl/error.h" #include "src/gl/shader.h" #include "src/gl/texture.h" #include "src/gl/vertex_array.h" #include "third_party/glew/include/GL/glew.h" namespace gl { // Makes the specified shader program active. inline void SetActiveShaderProgram(ShaderProgram& program) { gl_internal::CheckThreadSafety(); glUseProgram(program.id()); // This error check is necessary because glUseProgram can fail. gl_internal::ErrorCheck(); } // RAII version of the above: While live, makes the specified shader program // active. class ActiveShaderProgram final { public: explicit ActiveShaderProgram(ShaderProgram& program) { gl_internal::CheckThreadSafety(); glGetIntegerv(GL_CURRENT_PROGRAM, &old_); gl_internal::UnnecessaryErrorCheck(); SetActiveShaderProgram(program); } ActiveShaderProgram(const ActiveShaderProgram&) = delete; ActiveShaderProgram& operator=(const ActiveShaderProgram&) = delete; ~ActiveShaderProgram() { gl_internal::CheckThreadSafety(); glUseProgram(old_); // This error check is necessary because glUseProgram can fail. gl_internal::ErrorCheck(); } private: GLint old_; }; // Sets the specified uniform in the active shader. Throws std::logic_error in // the event of a type error (e.g., you set a mat4 uniform to a single integer). inline void SetActiveShaderUniform(int index, int v0) { gl_internal::CheckThreadSafety(); glUniform1i(index, v0); // This error check is necessary because the shader variable might have a // different type than was passed in. gl_internal::ErrorCheck(); } inline void SetActiveShaderUniform(int index, const Eigen::Vector2f& value) { gl_internal::CheckThreadSafety(); glUniform2fv(index, /*count=*/1, value.data()); // This error check is necessary because the shader variable might have a // different type than was passed in. gl_internal::ErrorCheck(); } inline void SetActiveShaderUniform(int index, const Eigen::Vector3f& value) { gl_internal::CheckThreadSafety(); glUniform3fv(index, /*count=*/1, value.data()); // This error check is necessary because the shader variable might have a // different type than was passed in. gl_internal::ErrorCheck(); } inline void SetActiveShaderUniform(int index, const Eigen::Matrix4f& value) { gl_internal::CheckThreadSafety(); glUniformMatrix4fv(index, /*count=*/1, /*transpose=*/GL_FALSE, value.data()); // This error check is necessary because the shader variable might have a // different type than was passed in. gl_internal::ErrorCheck(); } // Binds the specified VAO. inline void BindVertexArray(const VertexArray& vao) noexcept( !(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glBindVertexArray(vao.id()); gl_internal::UnnecessaryErrorCheck(); } // RAII version of the above: While live, binds the specified VAO. class BoundVertexArray final { public: explicit BoundVertexArray(const VertexArray& vao) noexcept( !(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_); gl_internal::UnnecessaryErrorCheck(); BindVertexArray(vao); } BoundVertexArray(const BoundVertexArray&) = delete; BoundVertexArray& operator=(const BoundVertexArray&) = delete; ~BoundVertexArray() noexcept(!(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glBindVertexArray(old_); gl_internal::UnnecessaryErrorCheck(); } private: GLint old_; }; // Sets the current texture unit. inline void UseTextureUnit(int unit) { gl_internal::CheckThreadSafety(); glActiveTexture(GL_TEXTURE0 + unit); // This error check is necessary because the user might have passed in a // too-large index for the texture unit. gl_internal::ErrorCheck(); } // Binds the specified 2D texture. inline void BindTexture2d(const Texture2d& tex) noexcept( !(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glBindTexture(GL_TEXTURE_2D, tex.id()); gl_internal::UnnecessaryErrorCheck(); } // RAII version of the above: While live, binds the specified 2D texture. class BoundTexture2d final { public: explicit BoundTexture2d(const Texture2d& tex) noexcept( !(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_); gl_internal::UnnecessaryErrorCheck(); BindTexture2d(tex); } BoundTexture2d(const BoundTexture2d&) = delete; BoundTexture2d& operator=(const BoundTexture2d&) = delete; ~BoundTexture2d() noexcept(!(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glBindTexture(GL_TEXTURE_2D, old_); gl_internal::UnnecessaryErrorCheck(); } private: GLint old_; }; enum class GlBuffer : GLenum { kColor = GL_COLOR_BUFFER_BIT, kDepth = GL_DEPTH_BUFFER_BIT, kStencil = GL_STENCIL_BUFFER_BIT, }; inline void Clear(const std::initializer_list& buffers) noexcept( !(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); GLenum mask = 0; for (GlBuffer b : buffers) { mask |= FromEnum(b); } glClear(mask); gl_internal::UnnecessaryErrorCheck(); } enum class Primitive : GLenum { kPoints = GL_POINTS, kLineStrip = GL_LINE_STRIP, kLineLoop = GL_LINE_LOOP, kLines = GL_LINES, kLineStripAdjacency = GL_LINE_STRIP_ADJACENCY, kLinesAdjacency = GL_LINES_ADJACENCY, kTriangleStrip = GL_TRIANGLE_STRIP, kTriangleFan = GL_TRIANGLE_FAN, kTriangles = GL_TRIANGLES, kTriangleStripAdjacency = GL_TRIANGLE_STRIP_ADJACENCY, kTrianglesAdjacency = GL_TRIANGLES_ADJACENCY, kPatches = GL_PATCHES, }; // Draws the elements from the specified VAO. The VAO must already be bound. void DrawElements(const gl::VertexArray&, Primitive); void SetViewport(int x, int y, int width, int height); } // namespace gl #endif // GLPLANET_SRC_GL_DRAW_H_