- Main Page
- Namespaces
- Classes
- Files

- File List

# PostFX.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/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

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