1 #include "ShaderProgram.h"
2 
3 #include <cassert>
4 #include <functional>
5 #include <iostream>
6 
7 #include "../client/human/HumanClientApp.h"
8 #include "../util/Logger.h"
9 
10 #include <boost/filesystem/fstream.hpp>
11 
12 
13 namespace {
CHECK_ERROR(const char * fn,const char * e)14     void CHECK_ERROR(const char* fn, const char* e) {
15         GLenum error = glGetError();
16         if (error != GL_NO_ERROR) {
17             const char* error_msg = "";
18 
19             switch(error) {
20                 case GL_INVALID_ENUM:      error_msg = "invalid enumerant"; break;
21                 case GL_INVALID_VALUE:     error_msg = "invalid value";     break;
22                 case GL_INVALID_OPERATION: error_msg = "invalid operation"; break;
23             }
24 
25             ErrorLogger() << fn << " () :"
26                                    << " GL error on " << e << ": "
27                                    << "'" << error_msg << "'";
28         }
29     }
30 
GetShaderLog(GLuint shader,std::string & log)31     void GetShaderLog(GLuint shader, std::string& log) {
32         log.clear();
33         GLint logSize;
34         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize);
35         CHECK_ERROR("GetShaderLog", "glGetShaderiv(GL_INFO_LOG_LENGTH)");
36         if (0 < logSize) {
37             log.resize(logSize, '\0');
38             GLsizei chars;
39             glGetShaderInfoLog(shader, logSize, &chars, &log[0]);
40             CHECK_ERROR("GetShaderLog", "glGetShaderInfoLog()");
41         }
42     }
43 
GetProgramLog(GLuint program,std::string & log)44     void GetProgramLog(GLuint program, std::string& log) {
45         log.clear();
46         GLint logSize;
47         glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logSize);
48         CHECK_ERROR("GetProgramLog", "glGetProgramiv(GL_INFO_LOG_LENGTH)");
49         if (0 < logSize) {
50             log.resize(logSize, '\0');
51             GLsizei chars;
52             glGetProgramInfoLog(program, logSize, &chars, &log[0]);
53             CHECK_ERROR("GetProgramLog", "glGetProgramInfoLog()");
54         }
55     }
56 }
57 
ReadFile(const boost::filesystem::path & path,std::string & file_contents)58 bool ReadFile(const boost::filesystem::path& path, std::string& file_contents) {
59     boost::filesystem::ifstream ifs(path);
60     if (!ifs)
61         return false;
62 
63     // skip byte order mark (BOM)
64     for (int BOM : {0xEF, 0xBB, 0xBF}) {
65         if (BOM != ifs.get()) {
66             // no header set stream back to start of file
67             ifs.seekg(0, std::ios::beg);
68             // and continue
69             break;
70         }
71     }
72 
73     std::getline(ifs, file_contents, '\0');
74 
75     // no problems?
76     return true;
77 }
78 
ShaderProgram(const std::string & vertex_shader,const std::string & fragment_shader)79 ShaderProgram::ShaderProgram(const std::string& vertex_shader, const std::string& fragment_shader) :
80     m_program_id(0),
81     m_vertex_shader_id(0),
82     m_fragment_shader_id(0),
83     m_link_succeeded(false),
84     m_program_log(),
85     m_program_validity_log(),
86     m_vertex_shader_log(),
87     m_fragment_shader_log()
88 {
89     glGetError();
90 
91     m_program_id = glCreateProgram();
92 
93     const char* strings[1] = { nullptr };
94 
95     if (!vertex_shader.empty()) {
96         m_vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
97         CHECK_ERROR("ShaderProgram::ShaderProgram", "glCreateShader(GL_VERTEX_SHADER)");
98 
99         strings[0] = vertex_shader.c_str();
100         glShaderSource(m_vertex_shader_id, 1, strings, nullptr);
101         CHECK_ERROR("ShaderProgram::ShaderProgram", "glShaderSource(vertex_shader)");
102 
103         glCompileShader(m_vertex_shader_id);
104         CHECK_ERROR("ShaderProgram::ShaderProgram", "glCompileShader(m_vertex_shader_id)");
105 
106         GetShaderLog(m_vertex_shader_id, m_vertex_shader_log);
107 
108         glAttachShader(m_program_id, m_vertex_shader_id);
109         CHECK_ERROR("ShaderProgram::ShaderProgram", "glAttachShader(m_vertex_shader_id)");
110     }
111 
112     if (!fragment_shader.empty()) {
113         m_fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
114         CHECK_ERROR("ShaderProgram::ShaderProgram", "glCreateShader(GL_FRAGMENT_SHADER)");
115 
116         strings[0] = fragment_shader.c_str();
117         glShaderSource(m_fragment_shader_id, 1, strings, nullptr);
118         CHECK_ERROR("ShaderProgram::ShaderProgram", "glShaderSource(fragment_shader)");
119 
120         glCompileShader(m_fragment_shader_id);
121         CHECK_ERROR("ShaderProgram::ShaderProgram", "glCompileShader(m_fragment_shader_id)");
122 
123         GetShaderLog(m_fragment_shader_id, m_fragment_shader_log);
124 
125         glAttachShader(m_program_id, m_fragment_shader_id);
126         CHECK_ERROR("ShaderProgram::ShaderProgram", "glAttachShader(m_fragment_shader_id)");
127     }
128 
129     GLint status;
130     glLinkProgram(m_program_id);
131     CHECK_ERROR("ShaderProgram::ShaderProgram", "glLinkProgram()");
132     glGetProgramiv(m_program_id, GL_LINK_STATUS, &status);
133     CHECK_ERROR("ShaderProgram::ShaderProgram", "glGetProgramiv(GL_LINK_STATUS)");
134     m_link_succeeded = status;
135 
136     GetProgramLog(m_program_id, m_program_log);
137 }
138 
shaderProgramFactory(const std::string & vertex_shader,const std::string & fragment_shader)139 std::unique_ptr<ShaderProgram> ShaderProgram::shaderProgramFactory(const std::string& vertex_shader,
140                                                                    const std::string& fragment_shader)
141 {
142     if (HumanClientApp::GetApp()->GLVersion() >= 2.0f)
143         return std::make_unique<ShaderProgram>(vertex_shader,fragment_shader);
144     return nullptr;
145 }
146 
~ShaderProgram()147 ShaderProgram::~ShaderProgram() {
148     glGetError();
149     if (glIsShader(m_vertex_shader_id)) {
150         glDeleteShader(m_vertex_shader_id);
151         CHECK_ERROR("ShaderProgram::~ShaderProgram", "glDeleteShader(m_vertex_shader_id)");
152     }
153     if (glIsShader(m_fragment_shader_id)) {
154         glDeleteShader(m_fragment_shader_id);
155         CHECK_ERROR("ShaderProgram::~ShaderProgram", "glDeleteShader(m_fragment_shader_id)");
156     }
157     if (glIsProgram(m_program_id)) {
158         glDeleteProgram(m_program_id);
159         CHECK_ERROR("ShaderProgram::~ShaderProgram", "glDeleteProgram()");
160     }
161 }
162 
ProgramID() const163 GLuint ShaderProgram::ProgramID() const
164 { return m_program_id; }
165 
LinkSucceeded() const166 bool ShaderProgram::LinkSucceeded() const
167 { return m_link_succeeded; }
168 
ProgramInfoLog() const169 const std::string& ShaderProgram::ProgramInfoLog() const
170 { return m_program_log; }
171 
ProgramValidityInfoLog() const172 const std::string& ShaderProgram::ProgramValidityInfoLog() const
173 { return m_program_validity_log; }
174 
VertexShaderInfoLog() const175 const std::string& ShaderProgram::VertexShaderInfoLog() const
176 { return m_vertex_shader_log; }
177 
FragmentShaderInfoLog() const178 const std::string& ShaderProgram::FragmentShaderInfoLog() const
179 { return m_fragment_shader_log; }
180 
Bind(const std::string & name,float f)181 void ShaderProgram::Bind(const std::string& name, float f) {
182     glGetError();
183     GLint location = glGetUniformLocation(m_program_id, name.c_str());
184     CHECK_ERROR("ShaderProgram::Bind", "glGetUniformLocation()");
185     assert(location != -1 &&
186            "Bind() : The named uniform variable does not exist.");
187     glUniform1f(location, f);
188     CHECK_ERROR("ShaderProgram::Bind", "glUniform1f()");
189 }
190 
Bind(const std::string & name,float f0,float f1)191 void ShaderProgram::Bind(const std::string& name, float f0, float f1) {
192     glGetError();
193     GLint location = glGetUniformLocation(m_program_id, name.c_str());
194     CHECK_ERROR("ShaderProgram::Bind", "glGetUniformLocation()");
195     assert(location != -1 &&
196            "Bind() : The named uniform variable does not exist.");
197     glUniform2f(location, f0, f1);
198     CHECK_ERROR("ShaderProgram::Bind", "glUniform2f()");
199 }
200 
Bind(const std::string & name,float f0,float f1,float f2)201 void ShaderProgram::Bind(const std::string& name, float f0, float f1, float f2) {
202     glGetError();
203     GLint location = glGetUniformLocation(m_program_id, name.c_str());
204     CHECK_ERROR("ShaderProgram::Bind", "glGetUniformLocation()");
205     assert(location != -1 &&
206            "Bind() : The named uniform variable does not exist.");
207     glUniform3f(location, f0, f1, f2);
208     CHECK_ERROR("ShaderProgram::Bind", "glUniform3f()");
209 }
210 
Bind(const std::string & name,float f0,float f1,float f2,float f3)211 void ShaderProgram::Bind(const std::string& name, float f0, float f1, float f2, float f3) {
212     glGetError();
213     GLint location = glGetUniformLocation(m_program_id, name.c_str());
214     CHECK_ERROR("ShaderProgram::Bind", "glGetUniformLocation()");
215     assert(location != -1 &&
216            "Bind() : The named uniform variable does not exist.");
217     glUniform4f(location, f0, f1, f2, f3);
218     CHECK_ERROR("ShaderProgram::Bind", "glUniform4f()");
219 }
220 
Bind(const std::string & name,std::size_t element_size,const std::vector<float> & floats)221 void ShaderProgram::Bind(const std::string& name, std::size_t element_size, const std::vector<float> &floats) {
222     assert(1 <= element_size && element_size <= 4);
223     assert((floats.size() % element_size) == 0);
224 
225     std::function<void (GLint, GLsizei, const GLfloat*)> functions[] =
226         { glUniform1fv,
227           glUniform2fv,
228           glUniform3fv,
229           glUniform4fv
230         };
231 
232     glGetError();
233     GLint location = glGetUniformLocation(m_program_id, name.c_str());
234     CHECK_ERROR("ShaderProgram::Bind", "glGetUniformLocation()");
235     assert(location != -1 &&
236            "Bind() : The named uniform variable does not exist.");
237     functions[element_size - 1](location,
238                                 floats.size() / element_size,
239                                 &floats[0]);
240     CHECK_ERROR("ShaderProgram::Bind", "glUniformNfv()");
241 }
242 
Bind(const std::string & name,GLuint texture_id)243 void ShaderProgram::Bind(const std::string& name, GLuint texture_id) {
244     glGetError();
245     GLint location = glGetUniformLocation(m_program_id, name.c_str());
246     CHECK_ERROR("ShaderProgram::Bind", "glGetUniformLocation()");
247     assert(location != -1 &&
248            "Bind() : The named uniform sampler does not exist.");
249     glUniform1i(location, texture_id);
250     CHECK_ERROR("ShaderProgram::Bind", "glUniform1i()");
251 }
252 
BindInt(const std::string & name,int i)253 void ShaderProgram::BindInt(const std::string& name, int i) {
254     glGetError();
255     GLint location = glGetUniformLocation(m_program_id, name.c_str());
256     CHECK_ERROR("ShaderProgram::BindInt", "glGetUniformLocation()");
257     assert(location != -1 &&
258            "BindInt() : The named uniform variable does not exist.");
259     glUniform1i(location, i);
260     CHECK_ERROR("ShaderProgram::BindInt", "glUniform1i()");
261 }
262 
BindInts(const std::string & name,int i0,int i1)263 void ShaderProgram::BindInts(const std::string& name, int i0, int i1) {
264     glGetError();
265     GLint location = glGetUniformLocation(m_program_id, name.c_str());
266     CHECK_ERROR("ShaderProgram::BindInts", "glGetUniformLocation()");
267     assert(location != -1 &&
268            "BindInts() : The named uniform variable does not exist.");
269     glUniform2i(location, i0, i1);
270     CHECK_ERROR("ShaderProgram::BindInts", "glUniform2i()");
271 }
272 
BindInts(const std::string & name,int i0,int i1,int i2)273 void ShaderProgram::BindInts(const std::string& name, int i0, int i1, int i2) {
274     glGetError();
275     GLint location = glGetUniformLocation(m_program_id, name.c_str());
276     CHECK_ERROR("ShaderProgram::BindInts", "glGetUniformLocation()");
277     assert(location != -1 &&
278            "BindInts() : The named uniform variable does not exist.");
279     glUniform3i(location, i0, i1, i2);
280     CHECK_ERROR("ShaderProgram::BindInts", "glUniform3i()");
281 }
282 
BindInts(const std::string & name,int i0,int i1,int i2,int i3)283 void ShaderProgram::BindInts(const std::string& name, int i0, int i1, int i2, int i3) {
284     glGetError();
285     GLint location = glGetUniformLocation(m_program_id, name.c_str());
286     CHECK_ERROR("ShaderProgram::BindInts", "glGetUniformLocation()");
287     assert(location != -1 &&
288            "BindInts() : The named uniform variable does not exist.");
289     glUniform4i(location, i0, i1, i2, i3);
290     CHECK_ERROR("ShaderProgram::BindInts", "glUniform4i()");
291 }
292 
BindInts(const std::string & name,std::size_t element_size,const std::vector<GLint> & ints)293 void ShaderProgram::BindInts(const std::string& name, std::size_t element_size, const std::vector<GLint> &ints) {
294     assert(1 <= element_size && element_size <= 4);
295     assert((ints.size() % element_size) == 0);
296 
297     std::function<void (GLint, GLsizei, const GLint*)> functions[] =
298         { glUniform1iv,
299           glUniform2iv,
300           glUniform3iv,
301           glUniform4iv
302         };
303 
304     glGetError();
305     GLint location = glGetUniformLocation(m_program_id, name.c_str());
306     CHECK_ERROR("ShaderProgram::BindInts", "glGetUniformLocation()");
307     assert(location != -1 &&
308            "BindInts() : The named uniform variable does not exist.");
309     functions[element_size - 1](location,
310                                 ints.size() / element_size,
311                                 &ints[0]);
312     CHECK_ERROR("ShaderProgram::BindInts", "glUniformNiv()");
313 }
314 
AllValuesBound()315 bool ShaderProgram::AllValuesBound() {
316     bool retval = false;
317     glGetError();
318     glValidateProgram(m_program_id);
319     CHECK_ERROR("ShaderProgram::AllValuesBound", "glValidateProgram()");
320     GLint status;
321     glGetProgramiv(m_program_id, GL_VALIDATE_STATUS, &status);
322     CHECK_ERROR("ShaderProgram::AllValuesBound", "glGetProgramiv(GL_VALIDATE_STATUS)");
323     retval = status;
324     GetProgramLog(m_program_id, m_program_validity_log);
325     return retval;
326 }
327 
Use()328 void ShaderProgram::Use() {
329     glGetError();
330     glUseProgram(m_program_id);
331     CHECK_ERROR("ShaderProgram::Use", "glUseProgram()");
332 }
333 
stopUse()334 void ShaderProgram::stopUse()
335 { glUseProgram(0); }
336