Back to index

libsfml  1.6+dfsg2
PostFX.cpp
Go to the documentation of this file.
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/PostFX.hpp>
00030 #include <SFML/Graphics/RenderWindow.hpp>
00031 #include <SFML/Graphics/GraphicsContext.hpp>
00032 #include <fstream>
00033 #include <iostream>
00034 #include <set>
00035 #include <sstream>
00036 
00037 
00038 namespace sf
00039 {
00043 PostFX::PostFX() :
00044 myShaderProgram(0)
00045 {
00046     // No filtering on frame buffer
00047     myFrameBuffer.SetSmooth(false);
00048 }
00049 
00050 
00054 PostFX::PostFX(const PostFX& Copy) :
00055 Drawable        (Copy),
00056 myShaderProgram (0),
00057 myTextures      (Copy.myTextures),
00058 myFragmentShader(Copy.myFragmentShader),
00059 myFrameBuffer   (Copy.myFrameBuffer)
00060 {
00061     // No filtering on frame buffer
00062     myFrameBuffer.SetSmooth(false);
00063 
00064     // Create the shaders and the program
00065     if (Copy.myShaderProgram)
00066         CreateProgram();
00067 }
00068 
00069 
00073 PostFX::~PostFX()
00074 {
00075     // Destroy effect program
00076     if (myShaderProgram)
00077     {
00078         // Make sure we have a valid context
00079         priv::GraphicsContext Ctx;
00080 
00081         GLCheck(glDeleteObjectARB(myShaderProgram));
00082     }
00083 }
00084 
00085 
00089 bool PostFX::LoadFromFile(const std::string& Filename)
00090 {
00091     // Open the file
00092     std::ifstream File(Filename.c_str());
00093     if (!File)
00094     {
00095         std::cerr << "Failed to open effect file \"" << Filename << "\"" << std::endl;
00096         return false;
00097     }
00098 
00099     // Apply the preprocessing pass to the fragment shader code
00100     myFragmentShader = PreprocessEffect(File);
00101 
00102     // Create the shaders and the program
00103     CreateProgram();
00104 
00105     return myShaderProgram != 0;
00106 }
00107 
00108 
00112 bool PostFX::LoadFromMemory(const std::string& Effect)
00113 {
00114     // Open a stream and copy the effect code
00115     std::istringstream Stream(Effect.c_str());
00116 
00117     // Apply the preprocessing pass to the fragment shader code
00118     myFragmentShader = PreprocessEffect(Stream);
00119 
00120     // Create the shaders and the program
00121     CreateProgram();
00122 
00123     return myShaderProgram != 0;
00124 }
00125 
00126 
00130 void PostFX::SetParameter(const std::string& Name, float X)
00131 {
00132     if (myShaderProgram)
00133     {
00134         // Enable program
00135         GLCheck(glUseProgramObjectARB(myShaderProgram));
00136 
00137         // Get parameter location and assign it new values
00138         GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00139         if (Location != -1)
00140             GLCheck(glUniform1fARB(Location, X));
00141         else
00142             std::cerr << "Parameter \"" << Name << "\" not found in post-effect" << std::endl;
00143 
00144         // Disable program
00145         GLCheck(glUseProgramObjectARB(0));
00146     }
00147 }
00148 
00149 
00153 void PostFX::SetParameter(const std::string& Name, float X, float Y)
00154 {
00155     if (myShaderProgram)
00156     {
00157         // Enable program
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(glUniform2fARB(Location, X, Y));
00164         else
00165             std::cerr << "Parameter \"" << Name << "\" not found in post-effect" << std::endl;
00166 
00167         // Disable program
00168         GLCheck(glUseProgramObjectARB(0));
00169     }
00170 }
00171 
00172 
00176 void PostFX::SetParameter(const std::string& Name, float X, float Y, float Z)
00177 {
00178     if (myShaderProgram)
00179     {
00180         // Enable program
00181         GLCheck(glUseProgramObjectARB(myShaderProgram));
00182 
00183         // Get parameter location and assign it new values
00184         GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00185         if (Location != -1)
00186             GLCheck(glUniform3fARB(Location, X, Y, Z));
00187         else
00188             std::cerr << "Parameter \"" << Name << "\" not found in post-effect" << std::endl;
00189 
00190         // Disable program
00191         GLCheck(glUseProgramObjectARB(0));
00192     }
00193 }
00194 
00195 
00199 void PostFX::SetParameter(const std::string& Name, float X, float Y, float Z, float W)
00200 {
00201     if (myShaderProgram)
00202     {
00203         // Enable program
00204         GLCheck(glUseProgramObjectARB(myShaderProgram));
00205 
00206         // Get parameter location and assign it new values
00207         GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00208         if (Location != -1)
00209             GLCheck(glUniform4fARB(Location, X, Y, Z, W));
00210         else
00211             std::cerr << "Parameter \"" << Name << "\" not found in post-effect" << std::endl;
00212 
00213         // Disable program
00214         GLCheck(glUseProgramObjectARB(0));
00215     }
00216 }
00217 
00218 
00222 void PostFX::SetTexture(const std::string& Name, Image* Texture)
00223 {
00224     // Check that the current texture unit is available
00225     GLint MaxUnits;
00226     GLCheck(glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB, &MaxUnits));
00227     if (myTextures.size() >= static_cast<std::size_t>(MaxUnits))
00228     {
00229         std::cerr << "Impossible to use texture \"" << Name << "\" for post-effect : all available texture units are used" << std::endl;
00230         return;
00231     }
00232 
00233     // Make sure the given name is a valid variable in the effect
00234     int Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00235     if (Location == -1)
00236     {
00237         std::cerr << "Texture \"" << Name << "\" not found in post-effect" << std::endl;
00238         return;
00239     }
00240 
00241     // Store the texture for later use
00242     myTextures[Name] = Texture ? Texture : &myFrameBuffer;
00243 }
00244 
00245 
00249 PostFX& PostFX::operator =(const PostFX& Other)
00250 {
00251     PostFX Temp(Other);
00252 
00253     std::swap(myShaderProgram,  Temp.myShaderProgram);
00254     std::swap(myTextures,       Temp.myTextures);
00255     std::swap(myFragmentShader, Temp.myFragmentShader);
00256     std::swap(myFrameBuffer,    Temp.myFrameBuffer);
00257 
00258     return *this;
00259 }
00260 
00261 
00265 bool PostFX::CanUsePostFX()
00266 {
00267     // Make sure we have a valid context
00268     priv::GraphicsContext Ctx;
00269 
00270     return glewIsSupported("GL_ARB_shading_language_100") != 0 &&
00271            glewIsSupported("GL_ARB_shader_objects")       != 0 &&
00272            glewIsSupported("GL_ARB_vertex_shader")        != 0 &&
00273            glewIsSupported("GL_ARB_fragment_shader")      != 0;
00274 }
00275 
00276 
00280 void PostFX::Render(RenderTarget& Target) const
00281 {
00282     // Check that we have a valid program
00283     if (!myShaderProgram)
00284         return;
00285 
00286     // Copy the current framebuffer pixels to our frame buffer texture
00287     // The ugly cast is temporary until PostFx are rewritten :)
00288     myFrameBuffer.CopyScreen((RenderWindow&)Target);
00289 
00290     // Enable program
00291     GLCheck(glUseProgramObjectARB(myShaderProgram));
00292 
00293     // Bind textures
00294     TextureTable::const_iterator ItTex = myTextures.begin();
00295     for (std::size_t i = 0; i < myTextures.size(); ++i)
00296     {
00297         int Location = glGetUniformLocationARB(myShaderProgram, ItTex->first.c_str());
00298         GLCheck(glUniform1iARB(Location, static_cast<GLint>(i)));
00299         GLCheck(glActiveTextureARB(static_cast<GLenum>(GL_TEXTURE0_ARB + i)));
00300         ItTex->second->Bind();
00301         ItTex++;
00302     }
00303 
00304     // Compute the texture coordinates (in case the texture is larger than the screen, or flipped)
00305     IntRect FrameBufferRect(0, 0, myFrameBuffer.GetWidth(), myFrameBuffer.GetHeight());
00306     FloatRect TexCoords = myFrameBuffer.GetTexCoords(FrameBufferRect);
00307 
00308     // Render a fullscreen quad using the effect on our framebuffer
00309     FloatRect Screen = Target.GetView().GetRect();
00310     glBegin(GL_QUADS);
00311          glTexCoord2f(TexCoords.Left,  TexCoords.Top);    glVertex2f(Screen.Left,  Screen.Bottom);
00312          glTexCoord2f(TexCoords.Right, TexCoords.Top);    glVertex2f(Screen.Right, Screen.Bottom);
00313          glTexCoord2f(TexCoords.Right, TexCoords.Bottom); glVertex2f(Screen.Right, Screen.Top);
00314          glTexCoord2f(TexCoords.Left,  TexCoords.Bottom); glVertex2f(Screen.Left,  Screen.Top);
00315     glEnd();
00316 
00317     // Disable program
00318     GLCheck(glUseProgramObjectARB(0));
00319 
00320     // Disable texture units
00321     for (std::size_t i = 0; i < myTextures.size(); ++i)
00322     {
00323         GLCheck(glActiveTextureARB(static_cast<GLenum>(GL_TEXTURE0_ARB + i)));
00324         GLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00325     }
00326     GLCheck(glActiveTextureARB(GL_TEXTURE0_ARB));
00327 }
00328 
00329 
00334 std::string PostFX::PreprocessEffect(std::istream& File)
00335 {
00336     // Initialize output string
00337     std::set<std::string> myTextures;
00338     std::string Out = "";
00339 
00340     // Variable declarations
00341     std::string Line;
00342     while (std::getline(File, Line) && (Line.substr(0, 6) != "effect"))
00343     {
00344         // Remove the ending '\r', if any
00345         if (!Line.empty() && (Line[Line.size() - 1] == '\r'))
00346             Line.erase(Line.size() - 1);
00347 
00348         // Skip empty lines
00349         if (Line == "")
00350             continue;
00351 
00352         // Extract variables type and name and convert them
00353         std::string Type, Name;
00354         std::istringstream iss(Line);
00355         if (!(iss >> Type >> Name))
00356         {
00357             std::cerr << "Post-effect error : invalid declaration (should be \"[type][name]\")" << std::endl
00358                       << "> " << Line << std::endl;
00359             return "";
00360         }
00361 
00362         if (Type == "texture")
00363         {
00364             // Textures need some checking and conversion
00365             if (myTextures.find(Name) != myTextures.end())
00366             {
00367                 std::cerr << "Post-effect error : texture \"" << Name << "\" already exists" << std::endl;
00368                 return "";
00369             }
00370 
00371             Out += "uniform sampler2D " + Name + ";\n";
00372             myTextures.insert(Name);
00373         }
00374         else
00375         {
00376             // Other types are just copied to output with "uniform" prefix
00377             Out += "uniform " + Type + " " + Name + ";\n";
00378         }
00379     }
00380 
00381     // Effect code
00382     Out += "void main()\n";
00383     while (std::getline(File, Line))
00384     {
00385         // Replace any texture lookup "T(" by "texture2D(T, "
00386         for (std::set<std::string>::const_iterator i = myTextures.begin(); i != myTextures.end(); ++i)
00387         {
00388             std::string::size_type Pos = Line.find(*i);
00389             if (Pos != std::string::npos)
00390                 Line.replace(Pos, i->size() + 1, "texture2D(" + *i + ", ");
00391         }
00392 
00393         // Replace "_in" by "gl_TexCoord[0].xy"
00394         for (std::string::size_type Pos = Line.find("_in"); Pos != std::string::npos; Pos = Line.find("_in"))
00395             Line.replace(Pos, 3, "gl_TexCoord[0].xy");
00396 
00397         // Replace "_out" by "gl_FragColor"
00398         for (std::string::size_type Pos = Line.find("_out"); Pos != std::string::npos; Pos = Line.find("_out"))
00399             Line.replace(Pos, 4, "gl_FragColor");
00400 
00401         // Write modified line to output string
00402         Out += Line + "\n";
00403     }
00404 
00405     return Out;
00406 }
00407 
00408 
00412 void PostFX::CreateProgram()
00413 {
00414     // Make sure we have a valid context
00415     priv::GraphicsContext Ctx;
00416 
00417     // Check that we can use post-FX !
00418     if (!CanUsePostFX())
00419     {
00420         std::cerr << "Failed to create a PostFX : your system doesn't support effects" << std::endl;
00421         return;
00422     }
00423 
00424     // Destroy effect program if it was already created
00425     if (myShaderProgram)
00426         GLCheck(glDeleteObjectARB(myShaderProgram));
00427 
00428     // Define vertex shader source (we provide it directly as it doesn't have to change)
00429     static const std::string VertexShaderSrc =
00430         "void main()"
00431         "{"
00432         "    gl_TexCoord[0] = gl_MultiTexCoord0;"
00433         "    gl_Position = ftransform();"
00434         "}";
00435 
00436     // Create the program
00437     myShaderProgram = glCreateProgramObjectARB();
00438 
00439     // Create the shaders
00440     GLhandleARB VertexShader   = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
00441     GLhandleARB FragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
00442 
00443     // Compile them
00444     const char* VertexSrc   = VertexShaderSrc.c_str();
00445     const char* FragmentSrc = myFragmentShader.c_str();
00446     GLCheck(glShaderSourceARB(VertexShader,   1, &VertexSrc,   NULL));
00447     GLCheck(glShaderSourceARB(FragmentShader, 1, &FragmentSrc, NULL));
00448     GLCheck(glCompileShaderARB(VertexShader));
00449     GLCheck(glCompileShaderARB(FragmentShader));
00450 
00451     // Check the compile logs
00452     GLint Success;
00453     GLCheck(glGetObjectParameterivARB(VertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &Success));
00454     if (Success == GL_FALSE)
00455     {
00456         char CompileLog[1024];
00457         GLCheck(glGetInfoLogARB(VertexShader, sizeof(CompileLog), 0, CompileLog));
00458         std::cerr << "Failed to compile post-effect :" << std::endl
00459                   << CompileLog << std::endl;
00460         GLCheck(glDeleteObjectARB(VertexShader));
00461         GLCheck(glDeleteObjectARB(FragmentShader));
00462         GLCheck(glDeleteObjectARB(myShaderProgram));
00463         myShaderProgram = 0;
00464         return;
00465     }
00466     GLCheck(glGetObjectParameterivARB(FragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &Success));
00467     if (Success == GL_FALSE)
00468     {
00469         char CompileLog[1024];
00470         GLCheck(glGetInfoLogARB(FragmentShader, sizeof(CompileLog), 0, CompileLog));
00471         std::cerr << "Failed to compile post-effect :" << std::endl
00472                   << CompileLog << std::endl;
00473         GLCheck(glDeleteObjectARB(VertexShader));
00474         GLCheck(glDeleteObjectARB(FragmentShader));
00475         GLCheck(glDeleteObjectARB(myShaderProgram));
00476         myShaderProgram = 0;
00477         return;
00478     }
00479 
00480     // Attach the shaders to the program
00481     GLCheck(glAttachObjectARB(myShaderProgram, VertexShader));
00482     GLCheck(glAttachObjectARB(myShaderProgram, FragmentShader));
00483 
00484     // We can now delete the shaders
00485     GLCheck(glDeleteObjectARB(VertexShader));
00486     GLCheck(glDeleteObjectARB(FragmentShader));
00487 
00488     // Link the program
00489     GLCheck(glLinkProgramARB(myShaderProgram));
00490 
00491     // Get link log
00492     GLCheck(glGetObjectParameterivARB(myShaderProgram, GL_OBJECT_LINK_STATUS_ARB, &Success));
00493     if (Success == GL_FALSE)
00494     {
00495         // Oops... link errors
00496         char LinkLog[1024];
00497         GLCheck(glGetInfoLogARB(myShaderProgram, sizeof(LinkLog), 0, LinkLog));
00498         std::cerr << "Failed to link post-effect :" << std::endl
00499                   << LinkLog << std::endl;
00500         GLCheck(glDeleteObjectARB(myShaderProgram));
00501         myShaderProgram = 0;
00502         return;
00503     }
00504 }
00505 
00506 } // namespace sf