PostFX.cpp
00001 00002 // 00003 // SFML - Simple and Fast Multimedia Library 00004 // Copyright (C) 2007-2009 Laurent Gomila ([email protected]) 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 ::