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