D:/Programmation/Cpp/SFML/src/SFML/AdvancedGraphics/PostFX.cpp

00001 
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007 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 
00026 // Headers
00028 #include <SFML/AdvancedGraphics/PostFX.hpp>
00029 #include <SFML/Graphics/GraphicsDevice.hpp>
00030 #include <SFML/Graphics/OpenGL.hpp>
00031 #include <SFML/Graphics/RenderWindow.hpp>
00032 #include <fstream>
00033 #include <iostream>
00034 #include <set>
00035 #include <sstream>
00036 
00037 
00041 sfPostFX::sfPostFX() :
00042 myShaderProgram(0)
00043 {
00044 
00045 }
00046 
00047 
00051 sfPostFX::sfPostFX(const std::string& Filename) :
00052 myShaderProgram(0)
00053 {
00054     LoadFromFile(Filename);
00055 }
00056 
00057 
00061 sfPostFX::~sfPostFX()
00062 {
00063     DestroyVideoResources();
00064 }
00065 
00066 
00070 void sfPostFX::LoadFromFile(const std::string& Filename)
00071 {
00072     // Load fragment shader source from file
00073     myFragmentShader = PreprocessEffect(Filename);
00074 
00075     // Initialize video resources if we have a valid rendering context
00076     if (sf_private::sfGraphicsDevice::GetInstance())
00077         InitVideoResources();
00078 }
00079 
00080 
00084 void sfPostFX::SetParameter(const std::string& Name, float X)
00085 {
00086     if (myShaderProgram)
00087     {
00088         // Enable program
00089         sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00090 
00091         // Get parameter location and assign it new values
00092         GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00093         if (Location != -1)
00094             sfGLCheck(glUniform1fARB(Location, X));
00095         else
00096             std::cerr << "Parameter \"" << Name << "\" not found in effect" << std::endl;
00097 
00098         // Disable program
00099         sfGLCheck(glUseProgramObjectARB(0));
00100     }
00101 }
00102 
00103 
00107 void sfPostFX::SetParameter(const std::string& Name, float X, float Y)
00108 {
00109     if (myShaderProgram)
00110     {
00111         // Enable program
00112         sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00113 
00114         // Get parameter location and assign it new values
00115         GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00116         if (Location != -1)
00117             sfGLCheck(glUniform2fARB(Location, X, Y));
00118         else
00119             std::cerr << "Parameter \"" << Name << "\" not found in effect" << std::endl;
00120 
00121         // Disable program
00122         sfGLCheck(glUseProgramObjectARB(0));
00123     }
00124 }
00125 
00126 
00130 void sfPostFX::SetParameter(const std::string& Name, float X, float Y, float Z)
00131 {
00132     if (myShaderProgram)
00133     {
00134         // Enable program
00135         sfGLCheck(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             sfGLCheck(glUniform3fARB(Location, X, Y, Z));
00141         else
00142             std::cerr << "Parameter \"" << Name << "\" not found in effect" << std::endl;
00143 
00144         // Disable program
00145         sfGLCheck(glUseProgramObjectARB(0));
00146     }
00147 }
00148 
00149 
00153 void sfPostFX::SetParameter(const std::string& Name, float X, float Y, float Z, float W)
00154 {
00155     if (myShaderProgram)
00156     {
00157         // Enable program
00158         sfGLCheck(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             sfGLCheck(glUniform4fARB(Location, X, Y, Z, W));
00164         else
00165             std::cerr << "Parameter \"" << Name << "\" not found in effect" << std::endl;
00166 
00167         // Disable program
00168         sfGLCheck(glUseProgramObjectARB(0));
00169     }
00170 }
00171 
00172 
00176 void sfPostFX::SetTexture(const std::string& Name, sfImage* Image)
00177 {
00178     // Just store the texture for later use
00179     mySamplers[Name] = Image ? Image : &myFrameBuffer;
00180 }
00181 
00182 
00186 void sfPostFX::Render(sfRenderWindow& Window)
00187 {
00188     // Check that we have a valid program
00189     if (!myShaderProgram)
00190         return;
00191 
00192     // If window dimensions have changed, recreate the frame buffer texture
00193     if ((Window.GetWidth() != myFrameBuffer.GetWidth()) || (Window.GetHeight() != myFrameBuffer.GetHeight()))
00194     {
00195         myFrameBuffer.Resize(Window.GetWidth(), Window.GetHeight());
00196         myFrameBuffer.SetSmooth(false);
00197         myFrameBuffer.SetRepeat(false);
00198     }
00199 
00200     // Reset projection matrix
00201     sfGLCheck(glMatrixMode(GL_PROJECTION));
00202     sfGLCheck(glPushMatrix());
00203     sfGLCheck(glLoadIdentity());
00204 
00205     // Disable color
00206     sfGLCheck(glColor4ub(255, 255, 255, 255));
00207 
00208     // Copy current framebuffer to our frame buffer texture
00209     myFrameBuffer.Bind();
00210     sfGLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, myFrameBuffer.GetWidth(), myFrameBuffer.GetHeight()));
00211 
00212     // Enable program
00213     sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00214 
00215     // Bind textures
00216     int Unit = 0;
00217     for (std::map<std::string, sfImage*>::iterator i = mySamplers.begin(); i != mySamplers.end(); ++i)
00218     {
00219         // Check that current texture unit is available
00220         if (Unit >= sf_private::sfGraphicsDevice::GetInstance()->GetCapabilities().MaxTextureUnits)
00221         {
00222             // Error : we have reached maximum texture units count
00223             std::cerr << "Impossible to use texture \"" << i->first << "\" for post-effect : all available texture units are used" << std::endl;
00224             break;
00225         }
00226 
00227         // Get the texture location in the effect
00228         int Location = glGetUniformLocationARB(myShaderProgram, i->first.c_str());
00229         if (Location != -1)
00230         {
00231             // Texture found : bind it and associate it to current texture unit
00232             sfGLCheck(glUniform1iARB(Location, Unit));
00233             sfGLCheck(glActiveTextureARB(GL_TEXTURE0_ARB + Unit));
00234             i->second->Bind();
00235             Unit++;
00236         }
00237         else
00238         {
00239             // Error : texture name not found in effect
00240             std::cerr << "Texture \"" << i->first << "\" not found in effect" << std::endl;
00241         }
00242     }
00243 
00244     // Compute texture coordinates (in case texture is larger than screen)
00245     sfIntRect FrameBufferRect(0, 0, myFrameBuffer.GetWidth(), myFrameBuffer.GetHeight());
00246     sfFloatRect TexCoords = myFrameBuffer.GetTexCoords(FrameBufferRect);
00247 
00248     // Render a fullscreen quad using effect on our framebuffer
00249     glBegin(GL_QUADS);
00250         glTexCoord2f(TexCoords.Left,  TexCoords.Top);    glVertex2f(-1.f, -1.f);
00251         glTexCoord2f(TexCoords.Left,  TexCoords.Bottom); glVertex2f(-1.f,  1.f);
00252         glTexCoord2f(TexCoords.Right, TexCoords.Bottom); glVertex2f( 1.f,  1.f);
00253         glTexCoord2f(TexCoords.Right, TexCoords.Top);    glVertex2f( 1.f, -1.f);
00254     glEnd();
00255 
00256     // Disable program
00257     sfGLCheck(glUseProgramObjectARB(0));
00258 
00259     // Disable texture units
00260     for (int i = 0; i < Unit; ++i)
00261     {
00262         sfGLCheck(glActiveTextureARB(GL_TEXTURE0_ARB + i));
00263         sfGLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00264     }
00265     sfGLCheck(glActiveTextureARB(GL_TEXTURE0_ARB));
00266 
00267     // Restore previous projection matrix
00268     sfGLCheck(glMatrixMode(GL_PROJECTION));
00269     sfGLCheck(glPopMatrix());
00270 }
00271 
00272 
00277 std::string sfPostFX::PreprocessEffect(const std::string& Filename)
00278 {
00279     // Open file to process
00280     std::ifstream File(Filename.c_str());
00281     if (!File)
00282     {
00283         std::cerr << "Failed to open effect file \"" << Filename << "\"" << std::endl;
00284         return "";
00285     }
00286 
00287     // Initialize output string
00288     std::set<std::string> myTextures;
00289     std::string Out = "";
00290 
00291     // Variable declarations
00292     std::string Line;
00293     while (std::getline(File, Line) && (Line.substr(0, 6) != "effect"))
00294     {
00295         // Skip enpty lines
00296         if (Line == "")
00297             continue;
00298 
00299         // Extract variables type and name and convert them
00300         std::string Type, Name;
00301         std::istringstream iss(Line);
00302         if (!(iss >> Type >> Name))
00303         {
00304             std::cerr << "Effect \"" << Filename << "\" : invalid declaration (should be \"[type][name]\")" << std::endl
00305                       << "> " << Line << std::endl;
00306             return "";
00307         }
00308 
00309         if (Type == "texture")
00310         {
00311             // Textures need some checking and conversion
00312             if (myTextures.find(Name) != myTextures.end())
00313             {
00314                 std::cerr << "Effect \"" << Filename << "\" : texture \"" << Name << "\" already exists" << std::endl;
00315                 return "";
00316             }
00317 
00318             Out += "uniform sampler2D " + Name + ";\n";
00319             myTextures.insert(Name);
00320         }
00321         else
00322         {
00323             // Other types are just copied to output with "uniform" prefix
00324             Out += "uniform " + Type + " " + Name + ";\n";
00325         }
00326     }
00327 
00328     // Effect code
00329     Out += "void main()\n";
00330     while (std::getline(File, Line))
00331     {
00332         // Replace any texture lookup "T(" by "texture2D(T, "
00333         for (std::set<std::string>::const_iterator i = myTextures.begin(); i != myTextures.end(); ++i)
00334         {
00335             std::string::size_type Pos = Line.find(*i);
00336             if (Pos != std::string::npos)
00337                 Line.replace(Pos, i->size() + 1, "texture2D(" + *i + ", ");
00338         }
00339 
00340         // Replace "_in" by "gl_TexCoord[0].xy"
00341         for (std::string::size_type Pos = Line.find("_in"); Pos != std::string::npos; Pos = Line.find("_in"))
00342             Line.replace(Pos, 3, "gl_TexCoord[0].xy");
00343 
00344         // Replace "_out" by "gl_FragColor"
00345         for (std::string::size_type Pos = Line.find("_out"); Pos != std::string::npos; Pos = Line.find("_out"))
00346             Line.replace(Pos, 4, "gl_FragColor");
00347 
00348         // Write modified line to output string
00349         Out += Line + "\n";
00350     }
00351 
00352     return Out;
00353 }
00354 
00355 
00359 bool sfPostFX::CreateAndAttachShader(const std::string& Source, unsigned int ShaderType)
00360 {
00361     // Create shader object
00362     GLhandleARB Shader = glCreateShaderObjectARB(ShaderType);
00363 
00364     // Load source
00365     int Length = static_cast<int>(Source.size());
00366     const char* Data = Source.data();
00367     sfGLCheck(glShaderSourceARB(Shader, 1, &Data, &Length));
00368 
00369     // Compile shader
00370     sfGLCheck(glCompileShaderARB(Shader));
00371 
00372     // Get compile log
00373     int Success;
00374     sfGLCheck(glGetObjectParameterivARB(Shader, GL_OBJECT_COMPILE_STATUS_ARB, &Success));
00375     if (Success == GL_FALSE)
00376     {
00377         // Oops... compile errors !
00378         char CompileLog[1024];
00379         sfGLCheck(glGetInfoLogARB(Shader, sizeof(CompileLog), 0, CompileLog));
00380         std::cerr << "Failed to compile effect :" << std::endl
00381                   << CompileLog << std::endl;
00382         sfGLCheck(glDeleteObjectARB(Shader));
00383         return false;
00384     }
00385 
00386     // Attach the shader to the program
00387     sfGLCheck(glAttachObjectARB(myShaderProgram, Shader));
00388 
00389     // We can now delete our shader
00390     sfGLCheck(glDeleteObjectARB(Shader));
00391 
00392     return true;
00393 }
00394 
00395 
00399 void sfPostFX::InitVideoResources()
00400 {
00401     // Check that we can use post-FX !
00402     if (!sf_private::sfGraphicsDevice::GetInstance()->GetCapabilities().CanDoPostFX)
00403     {
00404         std::cerr << "Failed to create a sfPostFX : your system doesn't support effects" << std::endl;
00405         return;
00406     }
00407 
00408     // Destroy effect program if it was already created
00409     if (myShaderProgram)
00410         sfGLCheck(glDeleteObjectARB(myShaderProgram));
00411 
00412     // Create the program
00413     myShaderProgram = glCreateProgramObjectARB();
00414 
00415     // Load vertex shader source (we provide it directly as it doesn't have to change)
00416     static const std::string VertexShader =
00417         "void main()"
00418         "{"
00419         "    gl_TexCoord[0] = gl_MultiTexCoord0;"
00420         "    gl_Position = ftransform();"
00421         "}";
00422 
00423     // Create vertex shader
00424     if (!CreateAndAttachShader(VertexShader, GL_VERTEX_SHADER_ARB))
00425     {
00426         sfGLCheck(glDeleteObjectARB(myShaderProgram));
00427         myShaderProgram = 0;
00428         return;
00429     }
00430 
00431     // Create fragment shader
00432     if (!CreateAndAttachShader(myFragmentShader, GL_FRAGMENT_SHADER_ARB))
00433     {
00434         sfGLCheck(glDeleteObjectARB(myShaderProgram));
00435         myShaderProgram = 0;
00436         return;
00437     }
00438 
00439     // Link the program
00440     sfGLCheck(glLinkProgramARB(myShaderProgram));
00441 
00442     // Get link log
00443     int Success;
00444     sfGLCheck(glGetObjectParameterivARB(myShaderProgram, GL_OBJECT_LINK_STATUS_ARB, &Success));
00445     if (Success == GL_FALSE)
00446     {
00447         // Oops... link errors !
00448         char LinkLog[1024];
00449         sfGLCheck(glGetInfoLogARB(myShaderProgram, sizeof(LinkLog), 0, LinkLog));
00450         std::cerr << "Failed to link effect :" << std::endl
00451                   << LinkLog << std::endl;
00452         sfGLCheck(glDeleteObjectARB(myShaderProgram));
00453         myShaderProgram = 0;
00454     }
00455 }
00456 
00457 
00461 void sfPostFX::DestroyVideoResources()
00462 {
00463     // Destroy effect program
00464     if (myShaderProgram)
00465     {
00466         sfGLCheck(glDeleteObjectARB(myShaderProgram));
00467         myShaderProgram = 0;
00468     }
00469 }

Generated for SFML by  doxygen 1.5.2