SFML logo
  • Main Page
  • Namespaces
  • Classes
  • Files
  • File List

Shader.cpp

00001 
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
00005 //
00006 // This software is provided 'as-is', without any express or implied warranty.
00007 // In no event will the authors be held liable for any damages arising from the use of this software.
00008 //
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it freely,
00011 // subject to the following restrictions:
00012 //
00013 // 1. The origin of this software must not be misrepresented;
00014 //    you must not claim that you wrote the original software.
00015 //    If you use this software in a product, an acknowledgment
00016 //    in the product documentation would be appreciated but is not required.
00017 //
00018 // 2. Altered source versions must be plainly marked as such,
00019 //    and must not be misrepresented as being the original software.
00020 //
00021 // 3. This notice may not be removed or altered from any source distribution.
00022 //
00024 
00025 
00027 // Headers
00029 #include <SFML/Graphics/Shader.hpp>
00030 #include <SFML/Graphics/GLCheck.hpp>
00031 #include <SFML/System/Err.hpp>
00032 #include <fstream>
00033 #include <sstream>
00034 
00035 
00036 namespace sf
00037 {
00039 // Static member data
00041 const Image Shader::CurrentTexture;
00042 
00043 
00045 Shader::Shader() :
00046 myShaderProgram (0),
00047 myCurrentTexture(-1)
00048 {
00049 
00050 }
00051 
00052 
00054 Shader::Shader(const Shader& copy) :
00055 myShaderProgram (0),
00056 myCurrentTexture(copy.myCurrentTexture),
00057 myTextures      (copy.myTextures),
00058 myFragmentShader(copy.myFragmentShader)
00059 {
00060     // Create the shaders and the program
00061     if (copy.myShaderProgram)
00062         CompileProgram();
00063 }
00064 
00065 
00067 Shader::~Shader()
00068 {
00069     // Destroy effect program
00070     if (myShaderProgram)
00071         GLCheck(glDeleteObjectARB(myShaderProgram));
00072 }
00073 
00074 
00076 bool Shader::LoadFromFile(const std::string& filename)
00077 {
00078     // Open the file
00079     std::ifstream file(filename.c_str());
00080     if (!file)
00081     {
00082         Err() << "Failed to open shader file \"" << filename << "\"" << std::endl;
00083         return false;
00084     }
00085 
00086     // Read the shader code from the file
00087     std::string line;
00088     while (std::getline(file, line))
00089         myFragmentShader += line + "\n";
00090 
00091     // Create the shaders and the program
00092     return CompileProgram();
00093 }
00094 
00095 
00097 bool Shader::LoadFromMemory(const std::string& shader)
00098 {
00099     // Save the shader code
00100     myFragmentShader = shader;
00101 
00102     // Create the shaders and the program
00103     return CompileProgram();
00104 }
00105 
00106 
00108 void Shader::SetParameter(const std::string& name, float x)
00109 {
00110     if (myShaderProgram)
00111     {
00112         // Enable program
00113         GLhandleARB program = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
00114         GLCheck(glUseProgramObjectARB(myShaderProgram));
00115 
00116         // Get parameter location and assign it new values
00117         GLint location = glGetUniformLocationARB(myShaderProgram, name.c_str());
00118         if (location != -1)
00119             GLCheck(glUniform1fARB(location, x));
00120         else
00121             Err() << "Parameter \"" << name << "\" not found in shader" << std::endl;
00122 
00123         // Disable program
00124         GLCheck(glUseProgramObjectARB(program));
00125     }
00126 }
00127 
00128 
00130 void Shader::SetParameter(const std::string& name, float x, float y)
00131 {
00132     if (myShaderProgram)
00133     {
00134         // Enable program
00135         GLhandleARB program = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
00136         GLCheck(glUseProgramObjectARB(myShaderProgram));
00137 
00138         // Get parameter location and assign it new values
00139         GLint location = glGetUniformLocationARB(myShaderProgram, name.c_str());
00140         if (location != -1)
00141             GLCheck(glUniform2fARB(location, x, y));
00142         else
00143             Err() << "Parameter \"" << name << "\" not found in shader" << std::endl;
00144 
00145         // Disable program
00146         GLCheck(glUseProgramObjectARB(program));
00147     }
00148 }
00149 
00150 
00152 void Shader::SetParameter(const std::string& name, float x, float y, float z)
00153 {
00154     if (myShaderProgram)
00155     {
00156         // Enable program
00157         GLhandleARB program = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
00158         GLCheck(glUseProgramObjectARB(myShaderProgram));
00159 
00160         // Get parameter location and assign it new values
00161         GLint location = glGetUniformLocationARB(myShaderProgram, name.c_str());
00162         if (location != -1)
00163             GLCheck(glUniform3fARB(location, x, y, z));
00164         else
00165             Err() << "Parameter \"" << name << "\" not found in shader" << std::endl;
00166 
00167         // Disable program
00168         GLCheck(glUseProgramObjectARB(program));
00169     }
00170 }
00171 
00172 
00174 void Shader::SetParameter(const std::string& name, float x, float y, float z, float w)
00175 {
00176     if (myShaderProgram)
00177     {
00178         // Enable program
00179         GLhandleARB program = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
00180         GLCheck(glUseProgramObjectARB(myShaderProgram));
00181 
00182         // Get parameter location and assign it new values
00183         GLint location = glGetUniformLocationARB(myShaderProgram, name.c_str());
00184         if (location != -1)
00185             GLCheck(glUniform4fARB(location, x, y, z, w));
00186         else
00187             Err() << "Parameter \"" << name << "\" not found in shader" << std::endl;
00188 
00189         // Disable program
00190         GLCheck(glUseProgramObjectARB(program));
00191     }
00192 }
00193 
00194 
00196 void Shader::SetParameter(const std::string& name, const Vector2f& v)
00197 {
00198     SetParameter(name, v.x, v.y);
00199 }
00200 
00201 
00203 void Shader::SetParameter(const std::string& name, const Vector3f& v)
00204 {
00205     SetParameter(name, v.x, v.y, v.z);
00206 }
00207 
00208 
00210 void Shader::SetTexture(const std::string& name, const Image& texture)
00211 {
00212     if (myShaderProgram)
00213     {
00214         // Check if there is a texture unit available
00215         GLint maxUnits;
00216         GLCheck(glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB, &maxUnits));
00217         if (myTextures.size() + 1 >= static_cast<std::size_t>(maxUnits))
00218         {
00219             Err() << "Impossible to use texture \"" << name << "\" for shader: all available texture units are used" << std::endl;
00220             return;
00221         }
00222 
00223         // Make sure the given name is a valid variable in the effect
00224         int location = glGetUniformLocationARB(myShaderProgram, name.c_str());
00225         if (location == -1)
00226         {
00227             Err() << "Texture \"" << name << "\" not found in shader" << std::endl;
00228             return;
00229         }
00230 
00231         // Store the texture for later use
00232         if (&texture != &CurrentTexture)
00233             myTextures[location] = &texture;
00234         else
00235             myCurrentTexture = location;
00236     }
00237 }
00238 
00239 
00241 void Shader::Bind() const
00242 {
00243     if (myShaderProgram)
00244     {
00245         // Enable the program
00246         GLCheck(glUseProgramObjectARB(myShaderProgram));
00247 
00248         // Bind the textures
00249         BindTextures();
00250 
00251         // Make sure that the texture unit which is left active is the number 0
00252         GLCheck(glActiveTextureARB(GL_TEXTURE0_ARB));
00253 
00254         // Bind the current texture
00255         if (myCurrentTexture != -1)
00256             GLCheck(glUniform1iARB(myCurrentTexture, 0));
00257     }
00258 }
00259 
00260 
00262 void Shader::Unbind() const
00263 {
00264     GLCheck(glUseProgramObjectARB(0));
00265 }
00266 
00267 
00269 Shader& Shader::operator =(const Shader& right)
00270 {
00271     Shader temp(right);
00272 
00273     std::swap(myShaderProgram,  temp.myShaderProgram);
00274     std::swap(myCurrentTexture, temp.myCurrentTexture);
00275     std::swap(myTextures,       temp.myTextures);
00276     std::swap(myFragmentShader, temp.myFragmentShader);
00277 
00278     return *this;
00279 }
00280 
00281 
00283 bool Shader::IsAvailable()
00284 {
00285     // Make sure that GLEW is initialized
00286     priv::EnsureGlewInit();
00287 
00288     return GLEW_ARB_shading_language_100 &&
00289            GLEW_ARB_shader_objects       &&
00290            GLEW_ARB_vertex_shader        &&
00291            GLEW_ARB_fragment_shader;
00292 }
00293 
00294 
00296 bool Shader::CompileProgram()
00297 {
00298     // First make sure that we can use shaders
00299     if (!IsAvailable())
00300     {
00301         Err() << "Failed to create a shader: your system doesn't support shaders "
00302               << "(you should test Shader::IsAvailable() before trying to use the Shader class)" << std::endl;
00303         return false;
00304     }
00305 
00306     // Make sure that GLEW is initialized (extra safety -- it is already done in IsAvailable())
00307     priv::EnsureGlewInit();
00308 
00309     // Destroy the shader if it was already created
00310     if (myShaderProgram)
00311         GLCheck(glDeleteObjectARB(myShaderProgram));
00312 
00313     // Define the vertex shader source (we provide it directly as it doesn't have to change)
00314     static const char* vertexSrc =
00315         "void main()"
00316         "{"
00317         "    gl_TexCoord[0] = gl_MultiTexCoord0;"
00318         "    gl_FrontColor = gl_Color;"
00319         "    gl_Position = ftransform();"
00320         "}";
00321 
00322     // Create the program
00323     myShaderProgram = glCreateProgramObjectARB();
00324 
00325     // Create the shaders
00326     GLhandleARB vertexShader   = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
00327     GLhandleARB fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
00328 
00329     // Compile them
00330     const char* fragmentSrc = myFragmentShader.c_str();
00331     GLCheck(glShaderSourceARB(vertexShader,   1, &vertexSrc,   NULL));
00332     GLCheck(glShaderSourceARB(fragmentShader, 1, &fragmentSrc, NULL));
00333     GLCheck(glCompileShaderARB(vertexShader));
00334     GLCheck(glCompileShaderARB(fragmentShader));
00335 
00336     // Check the compile logs
00337     GLint success;
00338     GLCheck(glGetObjectParameterivARB(vertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &success));
00339     if (success == GL_FALSE)
00340     {
00341         char log[1024];
00342         GLCheck(glGetInfoLogARB(vertexShader, sizeof(log), 0, log));
00343         Err() << "Failed to compile shader:" << std::endl
00344               << log << std::endl;
00345         GLCheck(glDeleteObjectARB(vertexShader));
00346         GLCheck(glDeleteObjectARB(fragmentShader));
00347         GLCheck(glDeleteObjectARB(myShaderProgram));
00348         myShaderProgram = 0;
00349         return false;
00350     }
00351     GLCheck(glGetObjectParameterivARB(fragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &success));
00352     if (success == GL_FALSE)
00353     {
00354         char log[1024];
00355         GLCheck(glGetInfoLogARB(fragmentShader, sizeof(log), 0, log));
00356         Err() << "Failed to compile shader:" << std::endl
00357               << log << std::endl;
00358         GLCheck(glDeleteObjectARB(vertexShader));
00359         GLCheck(glDeleteObjectARB(fragmentShader));
00360         GLCheck(glDeleteObjectARB(myShaderProgram));
00361         myShaderProgram = 0;
00362         return false;
00363     }
00364 
00365     // Attach the shaders to the program
00366     GLCheck(glAttachObjectARB(myShaderProgram, vertexShader));
00367     GLCheck(glAttachObjectARB(myShaderProgram, fragmentShader));
00368 
00369     // We can now delete the shaders
00370     GLCheck(glDeleteObjectARB(vertexShader));
00371     GLCheck(glDeleteObjectARB(fragmentShader));
00372 
00373     // Link the program
00374     GLCheck(glLinkProgramARB(myShaderProgram));
00375 
00376     // Get link log
00377     GLCheck(glGetObjectParameterivARB(myShaderProgram, GL_OBJECT_LINK_STATUS_ARB, &success));
00378     if (success == GL_FALSE)
00379     {
00380         // Oops... link errors
00381         char log[1024];
00382         GLCheck(glGetInfoLogARB(myShaderProgram, sizeof(log), 0, log));
00383         Err() << "Failed to link shader:" << std::endl
00384               << log << std::endl;
00385         GLCheck(glDeleteObjectARB(myShaderProgram));
00386         myShaderProgram = 0;
00387         return false;
00388     }
00389 
00390     return true;
00391 }
00392 
00393 
00395 void Shader::BindTextures() const
00396 {
00397     TextureTable::const_iterator it = myTextures.begin();
00398     for (std::size_t i = 0; i < myTextures.size(); ++i)
00399     {
00400         GLint index = static_cast<GLsizei>(i + 1);
00401         GLCheck(glUniform1iARB(it->first, index));
00402         GLCheck(glActiveTextureARB(GL_TEXTURE0_ARB + index));
00403         it->second->Bind();
00404         it++;
00405     }
00406 }
00407 
00408 
00410 void Shader::Use() const
00411 {
00412     BindTextures();
00413 }
00414 
00415 } // namespace sf

 ::  Copyright © 2007-2008 Laurent Gomila, all rights reserved  ::  Documentation generated by doxygen 1.5.2  ::