BARE2D
ShaderProgram.cpp
Go to the documentation of this file.
1 #include "ShaderProgram.hpp"
2 
3 #include <glm/glm.hpp>
4 #include <glm/gtc/type_ptr.hpp>
5 #include <GL/glew.h>
6 
7 #include "ResourceManager.hpp"
8 #include "IOManager.hpp"
9 #include "BAREErrors.hpp"
10 
11 namespace BARE2D
12 {
13 
14  ShaderProgram::ShaderProgram() : m_numberAttributes(0), m_programID(0), m_vertexShaderID(0), m_fragmentShaderID(0)
15  {
16 
17  }
18 
20  {
21  }
22 
23  void ShaderProgram::compileShaders(const char* vertexShaderPath, const char* fragmentShaderPath)
24  {
25  // Just read the two shaders and compile them
26  std::string vertexSource, fragmentSource;
27 
28  std::string fullPathVS = ResourceManager::getAssetsPathPrefix() + vertexShaderPath;
29  std::string fullPathFS = ResourceManager::getAssetsPathPrefix() + fragmentShaderPath;
30 
31  IOManager::readFileToBuffer(fullPathVS.c_str(), vertexSource);
32  IOManager::readFileToBuffer(fullPathFS.c_str(), fragmentSource);
33 
34  // Just compile them from their sources now
35  compileShadersFromSource(vertexSource.c_str(), fragmentSource.c_str());
36  }
37 
38  void ShaderProgram::compileShadersFromSource(const char* vertexSource, const char* fragmentSource)
39  {
40  // Make sure that if we already have a program, we don't anymore.
41  destroy();
42 
43  // Now to actually compile them
44  // Tell OpenGL to give us an ID
45  m_programID = glCreateProgram();
46 
47  // Check for errors
48  if(m_programID == 0) {
49  // Error occurred!
51  }
52 
53  // Do the same for the vertex and fragment shaders
54  m_vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
55 
56  // Check for errors
57  if(m_vertexShaderID == 0) {
58  // Error occurred!
60  }
61 
62  m_fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
63 
64  // Check for errors
65  if(m_fragmentShaderID == 0) {
66  // Error!
68  }
69 
70  // Compile each shader separately
71  compileShaderFromSource(vertexSource, "Vertex Shader", m_vertexShaderID);
72  compileShaderFromSource(fragmentSource, "Fragment Shader", m_fragmentShaderID);
73  }
74 
75  void ShaderProgram::linkShaders(std::initializer_list<std::string> attributes)
76  {
77  // Link up the shaders, and make sure that they have their needed attributes before they link.
78 
79  // Add the attributes
80  for(std::string item : attributes) {
81  glBindAttribLocation(m_programID, m_numberAttributes++, item.c_str());
82  }
83 
84  // Now actually link
85  // First, attach the shaders to the program
86  glAttachShader(m_programID, m_vertexShaderID);
87  glAttachShader(m_programID, m_fragmentShaderID);
88 
89  // Link them together
90  glLinkProgram(m_programID);
91 
92  // Check if they're linked now (error check)
93  GLint linked = GL_FALSE;
94  glGetProgramiv(m_programID, GL_LINK_STATUS, &linked);
95 
96  // Error check
97  if(linked == GL_FALSE) {
98  // Not linked! They should be, but they ain't.
99  // Get the length of the log
100  GLint maxLength = 0;
101  glGetProgramiv(m_programID, GL_INFO_LOG_LENGTH, &maxLength);
102 
103  // Get the actual log (null-terminated, but maxLength accounts for that)
104  std::vector<char> errorLog(maxLength);
105  glGetProgramInfoLog(m_programID, maxLength, &maxLength, &errorLog[0]);
106 
107  // Now that the program has failed and given us the info we want, we can dispose of it.
108  glDeleteProgram(m_programID);
109  glDeleteShader(m_vertexShaderID);
110  glDeleteShader(m_fragmentShaderID);
111 
112  // Throw fatal error, we cannot continue.
113  throwFatalError(BAREError::SHADER_LINK_FAILURE, "Shaders failed to link: " + std::string(errorLog.begin(), errorLog.end()));
114  }
115 
116  // Now that we have everything all linked up and it's all good, we can just detach the shaders and delete them. They're now in the program.
117  glDetachShader(m_programID, m_vertexShaderID);
118  glDetachShader(m_programID, m_fragmentShaderID);
119 
120  // And delete em
121  glDeleteShader(m_vertexShaderID);
122  glDeleteShader(m_fragmentShaderID);
123  }
124 
125  GLint ShaderProgram::getUniformLocation(const std::string& uniform)
126  {
127  // Just use OpenGL's calls, then check for errors
128  GLint location = glGetUniformLocation(m_programID, uniform.c_str());
129 
130  // Error check
131  if(location == (GLint)GL_INVALID_INDEX) {
132  // Error!
133  throwError(BAREError::UNIFORM_NOT_FOUND, "Uniform name: " + uniform);
134 
135  // Location, upon querying a non-existent uniform, will already be -1.
136  }
137 
138  return location;
139  }
140 
141  void ShaderProgram::bindFragOutputLocation(std::string outputVariableName, unsigned int location)
142  {
143  // Literally just call glBindFragDataLocation.
144  glBindFragDataLocation(m_programID, location, outputVariableName.c_str());
145  }
146 
148  {
149  // Enable the program!
150  glUseProgram(m_programID);
151 
152  // Enable all attributes!
153  for(unsigned int i = 0; i < m_numberAttributes; i++) {
154  glEnableVertexAttribArray(i);
155  }
156 
157  // We're done! gg ez
158  }
159 
161  {
162  // Do the opposite of use!
163  // Disable the program
164  glUseProgram(0);
165 
166  // Disable the attributes
167  for(unsigned int i = 0; i < m_numberAttributes; i++) {
168  glDisableVertexAttribArray(i);
169  }
170  }
171 
173  {
174  // Dispose of stuff
175  if(m_programID)
176  glDeleteProgram(m_programID);
177  m_programID = 0;
178  m_numberAttributes = 0;
179  m_vertexShaderID = 0;
180  m_fragmentShaderID = 0;
181  }
182 
183  void ShaderProgram::compileShaderFromSource(const char* source, const std::string& name, GLuint id)
184  {
185  // Tell OpenGL what our source is (1 is the number of source "files"), nullptr indicates that the source is null-terminated.
186  glShaderSource(id, 1, &source, nullptr);
187 
188  // Just compile
189  glCompileShader(id);
190 
191  // Check for errors of course
192  GLint compiled = GL_FALSE;
193  glGetShaderiv(id, GL_COMPILE_STATUS, &compiled);
194 
195  if(compiled == GL_FALSE) {
196  // Not compiled! It should be, but it ain't.
197  // Get the length of the log
198  GLint maxLength = 0;
199  glGetShaderiv(id, GL_INFO_LOG_LENGTH, &maxLength);
200 
201  // Get the actual log (null-terminated, but maxLength accounts for that)
202  std::vector<char> errorLog(maxLength);
203  glGetShaderInfoLog(id, maxLength, &maxLength, &errorLog[0]);
204 
205  // Now that the compilation has failed and given us the info we want, we can dispose of the shader.
206  glDeleteShader(id);
207 
208  // Throw fatal error, we cannot continue.
209  throwFatalError(BAREError::SHADER_COMPILE_FAILURE, "Shader failed to compile: " + name + "\n\n" + std::string(errorLog.begin(), errorLog.end()));
210  }
211  }
212 
213  bool ShaderProgram::doesUniformExist(const std::string uniform)
214  {
215  GLint loc = getUniformLocation(uniform);
216 
217  if(loc == -1) return false;
218  return true;
219  }
220 
221  // Template specializations
222 
223  template<>
224  void ShaderProgram::setUniform<int>(const std::string uniform, int* data, unsigned int num)
225  {
226  // First, get the location
227  GLint loc = getUniformLocation(uniform);
228 
229  // Now check for errors.
230  if(loc == -1) return;
231 
232  // Now, call glUniform{whatever}
233  glUniform1iv(loc, num, data);
234  }
235  template<>
236  void ShaderProgram::setUniform<unsigned int>(const std::string uniform, unsigned int* data, unsigned int num)
237  {
238  // First, get the location
239  GLint loc = getUniformLocation(uniform);
240 
241  // Now check for errors.
242  if(loc == -1) return;
243 
244  // Now, call glUniform{whatever}
245  glUniform1uiv(loc, num, data);
246  }
247  template<>
248  void ShaderProgram::setUniform<float>(const std::string uniform, float* data, unsigned int num)
249  {
250  // First, get the location
251  GLint loc = getUniformLocation(uniform);
252 
253  // Now check for errors.
254  if(loc == -1) return;
255 
256  // Now, call glUniform{whatever}
257  glUniform1fv(loc, num, data);
258  }
259 
260  template<>
261  void ShaderProgram::setUniform<glm::vec2>(const std::string uniform, glm::vec2* data, unsigned int num)
262  {
263  // First, get the location
264  GLint loc = getUniformLocation(uniform);
265 
266  // Now check for errors.
267  if(loc == -1) return;
268 
269  // Now, call glUniform{whatever}
270  glUniform2fv(loc, num, glm::value_ptr(*data));
271  }
272  template<>
273  void ShaderProgram::setUniform<glm::vec3>(const std::string uniform, glm::vec3* data, unsigned int num)
274  {
275  // First, get the location
276  GLint loc = getUniformLocation(uniform);
277 
278  // Now check for errors.
279  if(loc == -1) return;
280 
281  // Now, call glUniform{whatever}
282  glUniform3fv(loc, num, glm::value_ptr(*data));
283  }
284  template<>
285  void ShaderProgram::setUniform<glm::vec4>(const std::string uniform, glm::vec4* data, unsigned int num)
286  {
287  // First, get the location
288  GLint loc = getUniformLocation(uniform);
289 
290  // Now check for errors.
291  if(loc == -1) return;
292 
293  // Now, call glUniform{whatever}
294  glUniform4fv(loc, num, glm::value_ptr(*data));
295  }
296  template<>
297  void ShaderProgram::setUniform<glm::ivec2>(const std::string uniform, glm::ivec2* data, unsigned int num)
298  {
299  // First, get the location
300  GLint loc = getUniformLocation(uniform);
301 
302  // Now check for errors.
303  if(loc == -1) return;
304 
305  // Now, call glUniform{whatever}
306  glUniform2iv(loc, num, glm::value_ptr(*data));
307  }
308  template<>
309  void ShaderProgram::setUniform<glm::ivec3>(const std::string uniform, glm::ivec3* data, unsigned int num)
310  {
311  // First, get the location
312  GLint loc = getUniformLocation(uniform);
313 
314  // Now check for errors.
315  if(loc == -1) return;
316 
317  // Now, call glUniform{whatever}
318  glUniform3iv(loc, num, glm::value_ptr(*data));
319  }
320  template<>
321  void ShaderProgram::setUniform<glm::ivec4>(const std::string uniform, glm::ivec4* data, unsigned int num)
322  {
323  // First, get the location
324  GLint loc = getUniformLocation(uniform);
325 
326  // Now check for errors.
327  if(loc == -1) return;
328 
329  // Now, call glUniform{whatever}
330  glUniform4iv(loc, num, glm::value_ptr(*data));
331  }
332 
333  template<>
334  void ShaderProgram::setUniformMatrix<glm::mat4>(const std::string uniform, bool transpose, glm::mat4* data, unsigned int num)
335  {
336  // First, get the location
337  GLint loc = getUniformLocation(uniform);
338 
339  // Now check for errors.
340  if(loc == -1) return;
341 
342  // Now call glUniformMatrix{whatever}
343  glUniformMatrix4fv(loc, num, transpose, glm::value_ptr(*data));
344 
345  }
346 
347 }
BARE2D::ShaderProgram::use
void use()
Activates this shader program for the renderer to use.
Definition: ShaderProgram.cpp:147
BARE2D
Definition: App.cpp:13
BARE2D::BAREError::GLSL_PROGRAM_FAILURE
@ GLSL_PROGRAM_FAILURE
BARE2D::ShaderProgram::unuse
void unuse()
Deactivates this shader program.
Definition: ShaderProgram.cpp:160
BARE2D::BAREError::UNIFORM_NOT_FOUND
@ UNIFORM_NOT_FOUND
BARE2D::BAREError::SHADER_COMPILE_FAILURE
@ SHADER_COMPILE_FAILURE
BARE2D::ShaderProgram::m_programID
GLuint m_programID
Definition: ShaderProgram.hpp:88
BARE2D::ShaderProgram::ShaderProgram
ShaderProgram()
Definition: ShaderProgram.cpp:14
ResourceManager.hpp
BARE2D::ShaderProgram::compileShaders
void compileShaders(const char *vertexShaderPath, const char *fragmentShaderPath)
Compiles the shaders. Does not link them.
Definition: ShaderProgram.cpp:23
BARE2D::ShaderProgram::bindFragOutputLocation
void bindFragOutputLocation(std::string outputVariableName, unsigned int location)
A call to glBindFragDataLocation() - Causes an output variable of the fragment shader to output to a ...
Definition: ShaderProgram.cpp:141
BARE2D::ShaderProgram::m_fragmentShaderID
GLuint m_fragmentShaderID
Definition: ShaderProgram.hpp:90
BARE2D::ShaderProgram::~ShaderProgram
~ShaderProgram()
Definition: ShaderProgram.cpp:19
BARE2D::BAREError::VERTEX_SHADER_FAILURE
@ VERTEX_SHADER_FAILURE
BAREErrors.hpp
BARE2D::ShaderProgram::compileShaderFromSource
void compileShaderFromSource(const char *source, const std::string &name, GLuint id)
Compiles a shader from source with OpenGL id.
Definition: ShaderProgram.cpp:183
BARE2D::throwError
void throwError(BAREError err, std::string message)
Throws an error silently. Adds it to the pile.
Definition: BAREErrors.cpp:190
BARE2D::ShaderProgram::getUniformLocation
GLint getUniformLocation(const std::string &uniform)
Gets the location of a uniform in a program.
Definition: ShaderProgram.cpp:125
ShaderProgram.hpp
BARE2D::ShaderProgram::destroy
void destroy()
Releases all bound objects and deletes any allocated stuff.
Definition: ShaderProgram.cpp:172
BARE2D::IOManager::readFileToBuffer
static bool readFileToBuffer(std::string &filepath, std::vector< T > &buf, std::ios_base::openmode mode=std::ios::binary)
Loads a file into a buffer.
BARE2D::ResourceManager::getAssetsPathPrefix
static std::string getAssetsPathPrefix()
Returns the assets path prefix. Pretty simple.
Definition: ResourceManager.cpp:299
BARE2D::BAREError::FRAGMENT_SHADER_FAILURE
@ FRAGMENT_SHADER_FAILURE
BARE2D::BAREError::SHADER_LINK_FAILURE
@ SHADER_LINK_FAILURE
BARE2D::ShaderProgram::doesUniformExist
bool doesUniformExist(const std::string uniform)
Definition: ShaderProgram.cpp:213
BARE2D::ShaderProgram::linkShaders
void linkShaders(std::initializer_list< std::string > attributes)
Links the compiled shaders together to create a coherent shader program.
Definition: ShaderProgram.cpp:75
BARE2D::ShaderProgram::m_numberAttributes
unsigned int m_numberAttributes
Definition: ShaderProgram.hpp:86
IOManager.hpp
BARE2D::ShaderProgram::m_vertexShaderID
GLuint m_vertexShaderID
Definition: ShaderProgram.hpp:89
BARE2D::throwFatalError
void throwFatalError(BAREError err, std::string message)
Throws an error (fatal). Also calls displayErrors and exits the program.
Definition: BAREErrors.cpp:178
BARE2D::ShaderProgram::compileShadersFromSource
void compileShadersFromSource(const char *vertexSource, const char *fragmentSource)
Similar to compileShaders, this just compiles the shaders.
Definition: ShaderProgram.cpp:38