1 /*
2 Copyright (c) 2009 Andrew Caudwell (acaudwell@gmail.com)
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <boost/format.hpp>
29 #include <stdarg.h>
30
31 #ifndef USE_MGL_NAMESPACE
32 #include "gl.h"
33 #endif
34
35 #include "logger.h"
36
37 ShaderManager shadermanager;
38
39 //ShaderManager
40
ShaderManager()41 ShaderManager::ShaderManager() {
42 }
43
grab(const std::string & shader_prefix)44 Shader* ShaderManager::grab(const std::string& shader_prefix) {
45 Resource* s = resources[shader_prefix];
46
47 if(s==0) {
48 s = new Shader(shader_prefix);
49 resources[shader_prefix] = s;
50 }
51
52 s->addref();
53
54 return (Shader*) s;
55 }
56
manage(Shader * shader)57 void ShaderManager::manage(Shader* shader) {
58
59 if(shader->resource_name.empty()) {
60 throw ShaderException("Cannot manage shader with no resource name");
61 }
62
63 if(resources[shader->resource_name] != 0) {
64 throw ShaderException(str(boost::format("A shader resource already exists under the name '%s'") % shader->resource_name));
65 }
66
67 resources[shader->resource_name] = shader;
68
69 shader->addref();
70 }
71
unload()72 void ShaderManager::unload() {
73 for(std::map<std::string, Resource*>::iterator it= resources.begin(); it!=resources.end();it++) {
74 ((Shader*)it->second)->unload();
75 }
76 }
77
reload(bool force)78 void ShaderManager::reload(bool force) {
79 for(std::map<std::string, Resource*>::iterator it= resources.begin(); it!=resources.end();it++) {
80 ((Shader*)it->second)->reload(force);
81 }
82 }
83
84 // ShaderPass
85
ShaderPass(Shader * parent,int shader_object_type,const std::string & shader_object_desc)86 ShaderPass::ShaderPass(Shader* parent, int shader_object_type, const std::string& shader_object_desc) :
87 AbstractShaderPass(parent, shader_object_type, shader_object_desc) {
88 }
89
~ShaderPass()90 ShaderPass::~ShaderPass() {
91 unload();
92 }
93
unload()94 void ShaderPass::unload() {
95 if(shader_object!=0) glDeleteShader(shader_object);
96 shader_object = 0;
97 }
98
attachTo(unsigned int program)99 void ShaderPass::attachTo(unsigned int program) {
100 glAttachShader(program, shader_object);
101 }
102
103
checkError()104 void ShaderPass::checkError() {
105 if(!shader_object) return;
106
107 int compile_success;
108 glGetShaderiv(shader_object, GL_COMPILE_STATUS, &compile_success);
109
110 int info_log_length;
111 glGetShaderiv(shader_object, GL_INFO_LOG_LENGTH, &info_log_length);
112
113 const char* resource_desc = !parent->resource_name.empty() ? parent->resource_name.c_str() : "???";
114
115 if(info_log_length > 1) {
116 char info_log[info_log_length];
117
118 glGetShaderInfoLog(shader_object, info_log_length, &info_log_length, info_log);
119
120 std::string context;
121 if(!errorContext(info_log, context))
122 context = shader_object_source;
123
124 if(!compile_success) {
125 throw ShaderException(str(boost::format("%s shader '%s' failed to compile:\n%s\n%s")
126 % shader_object_desc % resource_desc % ((const char*)info_log) % context),
127 shader_object_source);
128 }
129
130 if(Logger::getDefault()->getLevel() == LOG_LEVEL_WARN) {
131 warnLog("%s shader '%s':\n%s\n%s",
132 shader_object_desc.c_str(),
133 resource_desc,
134 info_log,
135 context.c_str());
136
137 }
138
139 return;
140 }
141
142 if(!compile_success) {
143 throw ShaderException(str(boost::format("%s shader '%s' failed to compile") % shader_object_desc % resource_desc), shader_object_source);
144 }
145 }
146
compile()147 void ShaderPass::compile() {
148
149 if(!shader_object) shader_object = glCreateShader(shader_object_type);
150
151 if(source.empty()) return;
152
153 shader_object_source.clear();
154
155 toString(shader_object_source);
156
157 // apply subsitutions
158 parent->applySubstitutions(shader_object_source);
159
160 for(ShaderUniform* u: uniforms) {
161 u->setModified(false);
162 }
163
164 //fprintf(stderr, "src:\n%s", shader_object_source.c_str());
165
166 const char* source_ptr = shader_object_source.c_str();
167 int source_len = shader_object_source.size();
168
169 glShaderSource(shader_object, 1, (const GLchar**) &source_ptr, &source_len);
170 glCompileShader(shader_object);
171
172 checkError();
173 }
174
175
176 // Shader
177
Shader(const std::string & prefix)178 Shader::Shader(const std::string& prefix) : AbstractShader(prefix) {
179
180 loadPrefix();
181 }
182
Shader()183 Shader::Shader() : AbstractShader() {
184 }
185
~Shader()186 Shader::~Shader() {
187 clear();
188 }
189
unload()190 void Shader::unload() {
191 if(program != 0) glDeleteProgram(program);
192 program = 0;
193
194 for(std::map<std::string, ShaderUniform*>::iterator it= uniforms.begin(); it!=uniforms.end();it++) {
195 it->second->unload();
196 }
197 }
198
load()199 void Shader::load() {
200 //fprintf(stderr, "load\n");
201
202 if(program !=0) unload();
203
204 if(vertex_shader != 0) vertex_shader->compile();
205 if(geometry_shader != 0) geometry_shader->compile();
206 if(fragment_shader != 0) fragment_shader->compile();
207
208 program = glCreateProgram();
209
210 if(vertex_shader!=0) vertex_shader->attachTo(program);
211 if(geometry_shader!=0) geometry_shader->attachTo(program);
212 if(fragment_shader!=0) fragment_shader->attachTo(program);
213
214 glLinkProgram(program);
215
216 checkProgramError();
217
218 if(vertex_shader != 0) vertex_shader->unload();
219 if(geometry_shader != 0) geometry_shader->unload();
220 if(fragment_shader != 0) fragment_shader->unload();
221 }
222
loadPrefix()223 void Shader::loadPrefix() {
224
225 if(vertex_shader != 0) delete vertex_shader;
226 vertex_shader = 0;
227
228 if(fragment_shader != 0) delete fragment_shader;
229 fragment_shader = 0;
230
231 std::string shader_dir = shadermanager.getDir();
232
233 std::string vertex_file = shader_dir + prefix + std::string(".vert");
234 std::string fragment_file = shader_dir + prefix + std::string(".frag");
235
236 vertex_shader = new ShaderPass(this, GL_VERTEX_SHADER, "vertex");
237 vertex_shader->includeFile(vertex_file);
238
239 fragment_shader = new ShaderPass(this, GL_FRAGMENT_SHADER, "fragment");
240 fragment_shader->includeFile(fragment_file);
241
242 load();
243 }
244
checkProgramError()245 void Shader::checkProgramError() {
246
247 int link_success;
248 glGetProgramiv(program, GL_LINK_STATUS, &link_success);
249
250 int info_log_length;
251 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
252
253 const char* resource_desc = !resource_name.empty() ? resource_name.c_str() : "???";
254
255 if(info_log_length > 1) {
256 char info_log[info_log_length];
257 glGetProgramInfoLog(program, info_log_length, &info_log_length, info_log);
258
259 if(!link_success) {
260 errorLog("shader '%s' linking error:\n%s", resource_desc, info_log);
261 } else if(Logger::getDefault()->getLevel() == LOG_LEVEL_WARN) {
262 warnLog("shader '%s' warning:\n%s", resource_desc, info_log);
263 }
264 }
265
266 if(!link_success) {
267 throw ShaderException(str(boost::format("shader '%s' failed to link") % resource_desc));
268 }
269 }
270
bind()271 void Shader::bind() {
272 glUseProgram(program);
273 }
274
unbind()275 void Shader::unbind() {
276 glUseProgram(0);
277 }
278
getUniformLocation(const std::string & uniform_name)279 int Shader::getUniformLocation(const std::string& uniform_name) {
280 return glGetUniformLocation( program, uniform_name.c_str() );
281 }
282
applyUniform(ShaderUniform * u)283 void Shader::applyUniform(ShaderUniform* u) {
284
285 int location = u->getLocation();
286
287 if(location == -1) {
288 if(Logger::getDefault()->getLevel() == LOG_LEVEL_PEDANTIC) {
289 pedanticLog("shader '%s': invalid uniform '%s'", (!resource_name.empty() ? resource_name.c_str() : "???"), u->getName().c_str());
290 }
291 return;
292 }
293
294 switch(u->getType()) {
295 case SHADER_UNIFORM_INT:
296 glUniform1i(location, ((IntShaderUniform*)u)->getValue());
297 break;
298 case SHADER_UNIFORM_FLOAT:
299 glUniform1f(location, ((FloatShaderUniform*)u)->getValue());
300 break;
301 case SHADER_UNIFORM_BOOL:
302 glUniform1i(location, ((BoolShaderUniform*)u)->getValue());
303 break;
304 case SHADER_UNIFORM_SAMPLER_1D:
305 glUniform1i(location, ((Sampler1DShaderUniform*)u)->getValue());
306 break;
307 case SHADER_UNIFORM_SAMPLER_2D:
308 glUniform1i(location, ((Sampler2DShaderUniform*)u)->getValue());
309 break;
310 case SHADER_UNIFORM_VEC2:
311 glUniform2fv(location, 1, glm::value_ptr(((Vec2ShaderUniform*)u)->getValue()));
312 break;
313 case SHADER_UNIFORM_VEC3:
314 glUniform3fv(location, 1, glm::value_ptr(((Vec3ShaderUniform*)u)->getValue()));
315 break;
316 case SHADER_UNIFORM_VEC4:
317 glUniform4fv(location, 1, glm::value_ptr(((Vec4ShaderUniform*)u)->getValue()));
318 break;
319 case SHADER_UNIFORM_MAT3:
320 glUniformMatrix3fv(location, 1, 0, glm::value_ptr(((Mat3ShaderUniform*)u)->getValue()));
321 break;
322 case SHADER_UNIFORM_MAT4:
323 glUniformMatrix4fv(location, 1, 0, glm::value_ptr(((Mat4ShaderUniform*)u)->getValue()));
324 break;
325 case SHADER_UNIFORM_VEC2_ARRAY:
326 glUniform2fv(location, ((Vec2ArrayShaderUniform*)u)->getLength(), glm::value_ptr(((Vec2ArrayShaderUniform*)u)->getValue()[0]));
327 break;
328 case SHADER_UNIFORM_VEC3_ARRAY:
329 glUniform3fv(location, ((Vec3ArrayShaderUniform*)u)->getLength(), glm::value_ptr(((Vec3ArrayShaderUniform*)u)->getValue()[0]));
330 break;
331 case SHADER_UNIFORM_VEC4_ARRAY:
332 glUniform4fv(location, ((Vec4ArrayShaderUniform*)u)->getLength(), glm::value_ptr(((Vec4ArrayShaderUniform*)u)->getValue()[0]));
333 break;
334 default:
335 throw ShaderException(str(boost::format("unsupported uniform type %d") % u->getType()));
336 break;
337 }
338 }
339
grabShaderPass(unsigned int shader_object_type)340 AbstractShaderPass* Shader::grabShaderPass(unsigned int shader_object_type) {
341
342 AbstractShaderPass* shader_pass = 0;
343
344 switch(shader_object_type) {
345 case GL_VERTEX_SHADER:
346 if(!vertex_shader) vertex_shader = new ShaderPass(this, GL_VERTEX_SHADER, "vertex");
347 shader_pass = vertex_shader;
348 break;
349 case GL_GEOMETRY_SHADER_ARB:
350 if(!geometry_shader) geometry_shader = new ShaderPass(this, GL_GEOMETRY_SHADER_ARB, "geometry");
351 shader_pass = geometry_shader;
352 break;
353 case GL_FRAGMENT_SHADER:
354 if(!fragment_shader) fragment_shader = new ShaderPass(this, GL_FRAGMENT_SHADER, "fragment");
355 shader_pass = fragment_shader;
356 break;
357 }
358
359 return shader_pass;
360 }
361