summaryrefslogtreecommitdiff
path: root/src/gl/draw.h
blob: 13deb435517806f7089b37c38724508d1d1a8008 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// 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 <Eigen/Core>
#include <initializer_list>
#include <stdexcept>

#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<GlBuffer>& 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_