00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 #include "platform/shader.hpp"
00035 #include "platform/extensions.hpp"
00036 
00037 #include "data/log.hpp"
00038 #include "data/pointer.hpp"
00039 #include "data/array.hpp"
00040 #include "data/file.hpp"
00041 #include "data/fstream.hpp"
00042 
00043 namespace gsgl
00044 {
00045 
00046     using namespace data;
00047     using namespace io;
00048 
00049     namespace platform
00050     {
00051 
00052         static const int INFO_BUF_SIZE = 4096;
00053 
00054         
00055 
00056         class shader_base
00057         {
00058         protected:
00059             const string fname;
00060             int opengl_id;
00061             list<string> lines;
00062 
00063         public:
00064             shader_base(const string & fname);
00065             virtual ~shader_base();
00066 
00067             int get_id() { return opengl_id; }
00068             void compile();
00069 
00070             void read_file(const string & fname);
00071             void unload();
00072 
00073         protected:
00074             virtual void create() = 0;
00075         }; 
00076 
00077 
00078         
00079 
00080         
00081         class vertex_shader
00082             : public shader_base
00083         {
00084         public:
00085             vertex_shader(const gsgl::string & fname);
00086 
00087         protected:
00088             void create();
00089         }; 
00090 
00091 
00092         
00093 
00094 
00095         class fragment_shader
00096             : public shader_base
00097         {
00098         public:
00099             fragment_shader(const gsgl::string & fname);
00100 
00101         protected:
00102             void create();
00103         }; 
00104 
00105 
00106         
00107 
00108 
00109         shader_base::shader_base(const string & fname)
00110             : fname(fname), opengl_id(0)
00111         {
00112             read_file(fname);
00113         } 
00114 
00115 
00116         shader_base::~shader_base()
00117         {
00118             unload();
00119         } 
00120 
00121 
00122         void shader_base::read_file(const string & fname)
00123         {
00124             ft_stream f(fname);
00125             string line;
00126 
00127             for (f >> line; !f.at_end(); f >> line)
00128             {
00129                 lines.append(line + L"\n");
00130             }
00131         } 
00132 
00133 
00134         void shader_base::unload()
00135         {
00136             if (opengl_id)
00137             {
00138                 glDeleteShader(opengl_id);                                                                          CHECK_GL_ERRORS();
00139                 opengl_id = 0;
00140             }
00141         } 
00142 
00143 
00144         void shader_base::compile()
00145         {
00146             if (!opengl_id)
00147             {
00148                 this->create();
00149 
00150                 simple_array<char *> src_lines;
00151 
00152                 for (list<string>::iterator i = lines.iter(); i.is_valid(); ++i)
00153                 {
00154                     if (i->size())
00155                         src_lines.append(::strdup(i->c_string()));
00156                 }
00157 
00158                 glShaderSource(opengl_id, src_lines.size(), (const GLchar **) src_lines.ptr(), 0);                  CHECK_GL_ERRORS();
00159 
00160                 for (int i = 0; i < src_lines.size(); ++i)
00161                     ::free(src_lines[i]);
00162             }
00163 
00164             glCompileShader(opengl_id);                                                                             CHECK_GL_ERRORS();
00165 
00166             GLint compile_status;
00167             glGetShaderiv(opengl_id, GL_COMPILE_STATUS, &compile_status);                                           CHECK_GL_ERRORS();
00168 
00169             if (!compile_status)
00170             {
00171                 int len;
00172                 smart_pointer<char, true> buf(new char[INFO_BUF_SIZE]);
00173                 glGetShaderInfoLog(opengl_id, INFO_BUF_SIZE, &len, buf);                                            CHECK_GL_ERRORS();
00174 
00175                 string error_msg = fname + L"\n\n" + string(buf);
00176 
00177                 throw runtime_exception(error_msg.w_string());
00178             }
00179         } 
00180 
00181         
00182 
00183 
00184         vertex_shader::vertex_shader(const string & fname)
00185             : shader_base(fname)
00186         {
00187         } 
00188 
00189 
00190         void vertex_shader::create()
00191         {
00192             if (!opengl_id)
00193             {
00194                 opengl_id = glCreateShader(GL_VERTEX_SHADER);                                                       CHECK_GL_ERRORS();
00195 
00196                 if (!opengl_id)
00197                     throw runtime_exception(L"Unable to create vertex shader %ls", fname.w_string());
00198             }
00199         } 
00200 
00201 
00202         
00203 
00204 
00205         fragment_shader::fragment_shader(const string & fname)
00206             : shader_base(fname)
00207         {
00208         } 
00209 
00210 
00211         void fragment_shader::create()
00212         {
00213             if (!opengl_id)
00214             {
00215                 opengl_id = glCreateShader(GL_FRAGMENT_SHADER);                                                     CHECK_GL_ERRORS();
00216 
00217                 if (!opengl_id)
00218                     throw runtime_exception(L"Unable to create fragment shader %ls", fname.w_string());
00219             }
00220         } 
00221 
00222 
00223         
00224 
00225         shader_program::shader_program()
00226             : opengl_id(0)
00227         {
00228         } 
00229 
00230         
00231         shader_program::~shader_program()
00232         {
00233             unload();
00234 
00235             for (gsgl::data::list<shader_base *>::iterator i = shaders.iter(); i.is_valid(); ++i)
00236                 delete *i;
00237         } 
00238 
00239 
00240         void shader_program::add_vertex_shader(const string & fname)
00241         {
00242             shaders.append(new vertex_shader(io::file::get_full_path(fname)));
00243         } 
00244 
00245 
00246         void shader_program::add_fragment_shader(const string & fname)
00247         {
00248             shaders.append(new fragment_shader(io::file::get_full_path(fname)));
00249         } 
00250 
00251 
00252         void shader_program::load()
00253         {
00254             if (!opengl_id)
00255             {
00256                 opengl_id = glCreateProgram();                                                                      CHECK_GL_ERRORS();
00257 
00258                 if (!opengl_id && opengl_id != GL_INVALID_VALUE)
00259                     throw runtime_exception(L"Unable to create shader program.");
00260 
00261                 
00262                 for (list<shader_base *>::iterator i = shaders.iter(); i.is_valid(); ++i)
00263                 {
00264                     (*i)->compile();
00265                 }
00266 
00267                 
00268                 for (list<shader_base *>::iterator i = shaders.iter(); i.is_valid(); ++i)
00269                 {
00270                     glAttachShader(opengl_id, (*i)->get_id());                                                      CHECK_GL_ERRORS();
00271                 }
00272 
00273                 
00274                 int len, status;
00275                 smart_pointer<char, true> buf(new char[INFO_BUF_SIZE]);
00276 
00277                 glValidateProgram(opengl_id);                                                                       CHECK_GL_ERRORS();
00278                 glGetProgramiv(opengl_id, GL_VALIDATE_STATUS, &status);
00279 
00280                 if (!status)
00281                 {
00282                     glGetProgramInfoLog(opengl_id, INFO_BUF_SIZE, &len, buf);
00283                     throw runtime_exception(string(buf).w_string());
00284                 }
00285 
00286                 
00287                 glLinkProgram(opengl_id);                                                                           CHECK_GL_ERRORS();
00288                 glGetProgramiv(opengl_id, GL_LINK_STATUS, &status);
00289 
00290                 if (!status)
00291                 {
00292                     glGetProgramInfoLog(opengl_id, INFO_BUF_SIZE, &len, buf);
00293                     throw runtime_exception(string(buf).w_string());
00294                 }
00295             }
00296         } 
00297 
00298 
00299         void shader_program::unload()
00300         {
00301             if (opengl_id)
00302             {
00303                 for (list<shader_base *>::iterator i = shaders.iter(); i.is_valid(); ++i)
00304                     (*i)->unload();
00305 
00306                 glDeleteProgram(opengl_id);                                                                         CHECK_GL_ERRORS();
00307                 opengl_id = 0;
00308             }
00309         } 
00310 
00311 
00312         void shader_program::bind()
00313         {
00314             
00315             if (opengl_id)
00316             {
00317                 glUseProgram(opengl_id);                                                                            CHECK_GL_ERRORS();
00318             }
00319             else
00320             {
00321                 throw internal_exception(__FILE__, __LINE__, L"Unable to use shader program.");
00322             }
00323         } 
00324 
00325 
00326         void shader_program::unbind()
00327         {
00328             glUseProgram(0);                                                                                        CHECK_GL_ERRORS();
00329         } 
00330 
00331 
00332         
00333 
00334         void shader_program::set_uniform(const char *name, const int & i)
00335         {
00336             glUniform1i(get_uniform_loc(name), i);                                                                  CHECK_GL_ERRORS();
00337         } 
00338 
00339 
00340         void shader_program::set_uniform(const char *name, const float & f)
00341         {
00342             glUniform1f(get_uniform_loc(name), f);                                                                  CHECK_GL_ERRORS();
00343         } 
00344 
00345 
00346         void shader_program::set_uniform(const char *name, const float ff[4])
00347         {
00348             glUniform4fv(get_uniform_loc(name), 1, ff);                                                             CHECK_GL_ERRORS();
00349         } 
00350 
00351 
00352         void shader_program::set_uniform(const char *name, const bool b)
00353         {
00354             glUniform1i(get_uniform_loc(name), b);
00355         } 
00356 
00357 
00358         int shader_program::get_uniform_loc(const char *name)
00359         {
00360             assert(opengl_id);
00361             int loc = glGetUniformLocation(opengl_id, name);                                                        CHECK_GL_ERRORS();
00362             if (loc == -1)
00363                 throw runtime_exception(L"Unable to set uniform value '%hs' in shader program!", name);
00364             else
00365                 return loc;
00366         } 
00367 
00368     } 
00369 
00370 }