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