Engine
Frameworkcreatedbymeusableforthecreationofsimplegames.CurrentlysupportsOpenGL(Verysimple)andVulkan.
OpenGLShader.cpp
Go to the documentation of this file.
2 #ifdef USING_OPENGL
5 #include "Engine/engine.hpp"
8 
9 #include <assert.h>
10 #include <ThirdParty/glm/glm/gtc/type_ptr.hpp>
11 
12 namespace Engine
13 {
14 
16  // Compile shader and report success or failure
18  bool CompileShader(GLuint* shader, GLenum type, const GLchar* source)
19  {
20  GLint status;
21 
22  if (!source)
23  {
24  //LOG("Failed to load shader %s \n", source);
25  return false;
26  }
27 
28  *shader = glCreateShader(type);
29  glShaderSource(*shader, 1, &source, nullptr);
30  glGetError();
31  glCompileShader(*shader);
32 
33  GLint logLength = 0;
34  glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
35  if (logLength > 1)
36  {
37  GLchar *log = static_cast<GLchar *>(malloc(logLength));
38  glGetShaderInfoLog(*shader, logLength, &logLength, log);
39  eastl::string logInfo = log;
40  debug_info("OpenGLShader", "CompileShader", eastl::string("Shader compile log: \n " + logInfo));
41  free(log);
42  }
43 
44  glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
45  if (status == 0)
46  {
47  glDeleteShader(*shader);
48  return false;
49  }
50 
51  glGetError();
52 
53  return true;
54  }
55 
57  // Link Program
59  bool LinkProgram(GLuint prog)
60  {
61  GLint status;
62 
63  glLinkProgram(prog);
64 
65  GLint logLength = 0;
66  glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
67  if (logLength > 1)
68  {
69  GLchar *log = static_cast<GLchar *>(malloc(logLength));
70  glGetProgramInfoLog(prog, logLength, &logLength, log);
71  eastl::string logInfo = log;
72  debug_info("OpenGLShader", "LinkProgram", eastl::string("Program link log: \n " + logInfo));
73  free(log);
74  }
75 
76  glGetProgramiv(prog, GL_LINK_STATUS, &status);
77  if (status == 0)
78  return false;
79 
80  return true;
81  }
82 
84  // Validate program
86  bool ValidateProgram(GLuint prog)
87  {
88  GLint logLength = 0;
89  GLint status = 0;
90 
91  glValidateProgram(prog);
92  glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
93  if (logLength > 0)
94  {
95  GLchar *log = static_cast<GLchar *>(malloc(logLength));
96  glGetProgramInfoLog(prog, logLength, &logLength, log);
97  free(log);
98  }
99 
100  logLength = 0;
101  glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
102  if (status == 0)
103  {
104  return false;
105  }
106  return true;
107  }
108 
110  //
111  // ShaderParameter
112  //
114 
115  void ShaderParameter::SetValue(float val) const
116  {
117  if (!IsValid())
118  return;
119 
120 
121  assert(_type == GL_FLOAT);
122  glUniform1f(_location, val);
123  glGetError();
124  }
125 
126  void ShaderParameter::SetValue(int val) const
127  {
128  if (!IsValid())
129  return;
130 
131  assert(_type == GL_INT);
132  glUniform1i(_location, val);
133  glGetError();
134  }
135 
136  void ShaderParameter::SetValue(bool val) const
137  {
138  if (!IsValid())
139  return;
140 
141  assert(_type == GL_BOOL);
142  glUniform1i(_location, val);
143  glGetError();
144  }
145 
146  void ShaderParameter::SetValue(const glm::vec2& vec) const
147  {
148  if (!IsValid())
149  return;
150 
151  assert(_type == GL_FLOAT_VEC2);
152  glUniform2fv(_location, 1, &vec.x);
153  glGetError();
154  }
155 
156  void ShaderParameter::SetValue(const glm::vec3& vec) const
157  {
158  if (!IsValid())
159  return;
160 
161  assert(_type == GL_FLOAT_VEC3);
162  glUniform3fv(_location, 1, glm::value_ptr(vec));
163  glGetError();
164  }
165 
166  void ShaderParameter::SetValue(const glm::vec4& vec) const
167  {
168  if (!IsValid())
169  return;
170 
171  assert(_type == GL_FLOAT_VEC4);
172  glUniform4fv(_location, 1, &vec.x);
173  glGetError();
174  }
175 
176  void ShaderParameter::SetValue(const glm::mat4x4& mtx, bool transpose) const
177  {
178  if (!IsValid())
179  return;
180 
181  assert(_type == GL_FLOAT_MAT4);
182  glUniformMatrix4fv(_location, 1, transpose, glm::value_ptr(mtx));
183  glGetError();
184  }
185 
186  void ShaderParameter::SetValue(Texture &texture_) const
187  {
188  if (!IsValid())
189  return;
190 
191  assert(_type == GL_SAMPLER_2D);
192  // Use texture with index sampler. GL_TEXTURE1 = GL_TEXTURE10+1 is always true
193  glActiveTexture(GL_TEXTURE0 + _sampler);
194  glGetError();
195  // Work with this texture
196  glBindTexture(GL_TEXTURE_2D, GLuint(texture_.GetTexture()));
197  glGetError();
198  // Set the sampler
199  glUniform1i(_location, _sampler);
200  glGetError();
201  }
202 
204  //
205  // ShaderAttribute
206  //
208 
209  void ShaderAttribute::SetAttributePointer(GLint size,
210  GLenum type,
211  GLboolean normalized,
212  GLsizei stride,
213  const GLvoid *pointer) const
214  {
215  if (!IsValid())
216  return;
217 
218  glVertexAttribPointer(
219  _location, // attribute
220  size, // number of elements per vertex element
221  type, // the type of each element
222  normalized, // take our values as-is or normalize
223  stride, // no extra data between each position
224  pointer // offset of first element
225  );
226  glGetError();
227 
228  glEnableVertexAttribArray(_location);
229  glGetError();
230  }
231 
232  void ShaderAttribute::DisableAttributePointer() const
233  {
234  if (!IsValid())
235  return;
236 
237  glDisableVertexAttribArray(_location);
238  glGetError();
239  }
240 
242  //
243  // Shader
244  //
246 
247  GLuint LoadShader(eastl::string filePath, int shaderType)
248  {
249  GLuint shader;
250  CompileShader(&shader, shaderType, reinterpret_cast<char const *const>(Utility::ReadFile(filePath, std::ios::binary).data()));
251  return shader;
252  }
253 
254  OpenGLShader::OpenGLShader(const eastl::string& vertexFileName, const eastl::string& fragmentFileName) :
255  OpenGLShader(vertexFileName, fragmentFileName, "")
256  {}
257 
258  OpenGLShader::OpenGLShader(const eastl::string& vertexFileName,
259  const eastl::string& fragmentFileName,
260  const eastl::string& geometryFileName)
261  {
262  if (!LoadSource(vertexFileName, fragmentFileName, geometryFileName))
263  return;
264  }
265 
266  bool OpenGLShader::LoadSource(const eastl::string& vertexShader, const eastl::string& fragmentShader,
267  const eastl::string& geometryShader)
268  {
269  program = glCreateProgram();
270 
271  eastl::string shaderPath = "Resources/Shaders/";
272  GLuint vertShader = LoadShader(shaderPath + vertexShader, GL_VERTEX_SHADER);
273  GLuint fragShader = LoadShader(shaderPath + fragmentShader, GL_FRAGMENT_SHADER);
274  GLuint geomShader = 0;
275 
276  if (geometryShader.length() > 0)
277  geomShader = LoadShader(shaderPath + geometryShader, GL_GEOMETRY_SHADER);
278 
279  // Attach vertex shader to program
280  glAttachShader(program, vertShader);
281  glGetError();
282 
283  // Attach geometry shader to program
284  if (geomShader)
285  glAttachShader(program, geomShader);
286 
287  // Attach fragment shader to program
288  glAttachShader(program, fragShader);
289  glGetError();
290 
291  // Link program
292  if (!LinkProgram(program))
293  {
294  if (vertShader)
295  {
296  glDeleteShader(vertShader);
297  vertShader = 0;
298  }
299  if (fragShader)
300  {
301  glDeleteShader(fragShader);
302  fragShader = 0;
303  }
304  if (geomShader)
305  {
306  glDeleteShader(geomShader);
307  geomShader = 0;
308  }
309  if (program)
310  {
311  glDeleteProgram(program);
312  program = 0;
313  }
314 
315  glGetError();
316 
317  // We crash here, else the logs will be flooded with repeated
318  // error messages.
319  _ASSERT(false);
320 
321  return false;
322  }
323 
324  glDeleteShader(vertShader);
325  glGetError();
326 
327  glDeleteShader(geomShader);
328  glGetError();
329 
330  glDeleteShader(fragShader);
331  glGetError();
332 
333  LoadParameters();
334 
335  return true;
336  }
337 
338  eastl::weak_ptr<ShaderParameter> OpenGLShader::GetParameter(const eastl::string& name)
339  {
340  // Try to find param
341  auto itr = parameters.find(name);
342  if (itr != parameters.end())
343  return itr->second;
344 
345  // Create and return a non-valid param that is stored in collection
346  // in case it becomes valid after a reload
347  parameters[name] = eastl::shared_ptr<ShaderParameter>();
348  return parameters[name];
349  }
350 
351  eastl::weak_ptr<ShaderAttribute> OpenGLShader::GetAttribute(const eastl::string& name)
352  {
353  // Try to find param
354  auto itr = attributes.find(name);
355  if (itr != attributes.end())
356  return itr->second;
357 
358  // Create and return a non-valid param that is stored in collection
359  // in case it becomes valid after a reload
360  attributes[name] = eastl::shared_ptr<ShaderAttribute>();
361  return attributes[name];
362  }
363 
364  GLuint OpenGLShader::GetProgram() const
365  {
366  return program;
367  }
368 
369  void OpenGLShader::Activate()
370  {
371  glUseProgram(program);
372  glGetError();
373  }
374 
375  void OpenGLShader::Deactivate()
376  {
377  for (auto& a : attributes)
378  a.second->DisableAttributePointer();
379  }
380 
381  void OpenGLShader::LoadParameters()
382  {
385  for (auto& itr : parameters)
386  itr.second->Invalidate();
387 
388  // Get the number of uniforms and resize the parameters collection accordingly
389  GLint numActiveUniforms = 0;
390  glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numActiveUniforms);
391  //parameters.resize(numActiveUniforms);
392 
393  // Get the maximum name length
394  GLint maxUniformNameLength = 0;
395  glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformNameLength);
396  eastl::vector<GLchar> uniformNameData(maxUniformNameLength);
397 
398  // Go over all the uniforms
399  int samplerCount = 0;
400  for (int uniform = 0; uniform < numActiveUniforms; uniform++)
401  {
402  GLint arraySize = 0;
403  GLenum type = 0;
404  GLsizei actualLength = 0;
405  glGetActiveUniform(
406  program,
407  uniform,
408  maxUniformNameLength,
409  &actualLength,
410  &arraySize,
411  &type,
412  &uniformNameData[0]);
413  eastl::string name(&uniformNameData[0], actualLength);
414  GLint location = glGetUniformLocation(program, name.c_str());
415 
416  auto itr = parameters.find(name);
417  if (itr != parameters.end())
418  {
419  if (type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE)
420  itr->second->Reset(this, name, type, location, samplerCount++);
421  else
422  itr->second->Reset(this, name, type, location);
423  }
424  else
425  {
426  eastl::shared_ptr<ShaderParameter> param;
427  if (type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE)
428  param = eastl::shared_ptr<ShaderParameter>(new ShaderParameter(this, name, type, location, samplerCount++));
429  else
430  param = eastl::shared_ptr<ShaderParameter>(new ShaderParameter(this, name, type, location));
431  parameters[name] = param;
432  }
433  }
434 
435 
436  GLint numActiveAttribs = 0;
437  glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &numActiveAttribs);
438 
439 
440  GLint maxAttribNameLength = 0;
441  glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttribNameLength);
442  eastl::vector<GLchar> attribNameData(maxAttribNameLength);
443 
444 
445  for (int attrib = 0; attrib < numActiveAttribs; ++attrib)
446  {
447  GLint arraySize = 0;
448  GLenum type = 0;
449  GLsizei actualLength = 0;
450  glGetActiveAttrib(program,
451  attrib,
452  GLsizei(attribNameData.size()),
453  &actualLength,
454  &arraySize,
455  &type,
456  &attribNameData[0]);
457  eastl::string name(static_cast<char*>(&attribNameData[0]));
458  GLint location = glGetAttribLocation(program, name.c_str());
459 
460  auto itr = attributes.find(name);
461  if (itr != attributes.end())
462  {
463  itr->second->Reset(this, name, type, location);
464  }
465  else
466  {
467  attributes[name] = eastl::shared_ptr<ShaderAttribute>(new ShaderAttribute(this, name, type, location));
468  }
469  }
470  }
471 
472  bool OpenGLShader::Validate() const
473  {
474  if (!ValidateProgram(program))
475  {
476  //LOG("Failed to validate program: %d", _program);
477  glGetError();
478  return false;
479  }
480  return true;
481  }
482 } //namespace Engine
483 #endif
#define debug_info(debug_class, function, value)
This functions logs a debug info to the log and console. Use this for useless information.
Definition: Logging.hpp:60
static eastl::vector< char > ReadFile(const eastl::string &fileName, int fileOpenMode=1)
Definition: Utility.cpp:7