00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00024
00026
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
00073 myFragmentShader = PreprocessEffect(Filename);
00074
00075
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
00089 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00090
00091
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
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
00112 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00113
00114
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
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
00135 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00136
00137
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
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
00158 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00159
00160
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
00168 sfGLCheck(glUseProgramObjectARB(0));
00169 }
00170 }
00171
00172
00176 void sfPostFX::SetTexture(const std::string& Name, sfImage* Image)
00177 {
00178
00179 mySamplers[Name] = Image ? Image : &myFrameBuffer;
00180 }
00181
00182
00186 void sfPostFX::Render(sfRenderWindow& Window)
00187 {
00188
00189 if (!myShaderProgram)
00190 return;
00191
00192
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
00201 sfGLCheck(glMatrixMode(GL_PROJECTION));
00202 sfGLCheck(glPushMatrix());
00203 sfGLCheck(glLoadIdentity());
00204
00205
00206 sfGLCheck(glColor4ub(255, 255, 255, 255));
00207
00208
00209 myFrameBuffer.Bind();
00210 sfGLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, myFrameBuffer.GetWidth(), myFrameBuffer.GetHeight()));
00211
00212
00213 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00214
00215
00216 int Unit = 0;
00217 for (std::map<std::string, sfImage*>::iterator i = mySamplers.begin(); i != mySamplers.end(); ++i)
00218 {
00219
00220 if (Unit >= sf_private::sfGraphicsDevice::GetInstance()->GetCapabilities().MaxTextureUnits)
00221 {
00222
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
00228 int Location = glGetUniformLocationARB(myShaderProgram, i->first.c_str());
00229 if (Location != -1)
00230 {
00231
00232 sfGLCheck(glUniform1iARB(Location, Unit));
00233 sfGLCheck(glActiveTextureARB(GL_TEXTURE0_ARB + Unit));
00234 i->second->Bind();
00235 Unit++;
00236 }
00237 else
00238 {
00239
00240 std::cerr << "Texture \"" << i->first << "\" not found in effect" << std::endl;
00241 }
00242 }
00243
00244
00245 sfIntRect FrameBufferRect(0, 0, myFrameBuffer.GetWidth(), myFrameBuffer.GetHeight());
00246 sfFloatRect TexCoords = myFrameBuffer.GetTexCoords(FrameBufferRect);
00247
00248
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
00257 sfGLCheck(glUseProgramObjectARB(0));
00258
00259
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
00268 sfGLCheck(glMatrixMode(GL_PROJECTION));
00269 sfGLCheck(glPopMatrix());
00270 }
00271
00272
00277 std::string sfPostFX::PreprocessEffect(const std::string& Filename)
00278 {
00279
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
00288 std::set<std::string> myTextures;
00289 std::string Out = "";
00290
00291
00292 std::string Line;
00293 while (std::getline(File, Line) && (Line.substr(0, 6) != "effect"))
00294 {
00295
00296 if (Line == "")
00297 continue;
00298
00299
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
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
00324 Out += "uniform " + Type + " " + Name + ";\n";
00325 }
00326 }
00327
00328
00329 Out += "void main()\n";
00330 while (std::getline(File, Line))
00331 {
00332
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
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
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
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
00362 GLhandleARB Shader = glCreateShaderObjectARB(ShaderType);
00363
00364
00365 int Length = static_cast<int>(Source.size());
00366 const char* Data = Source.data();
00367 sfGLCheck(glShaderSourceARB(Shader, 1, &Data, &Length));
00368
00369
00370 sfGLCheck(glCompileShaderARB(Shader));
00371
00372
00373 int Success;
00374 sfGLCheck(glGetObjectParameterivARB(Shader, GL_OBJECT_COMPILE_STATUS_ARB, &Success));
00375 if (Success == GL_FALSE)
00376 {
00377
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
00387 sfGLCheck(glAttachObjectARB(myShaderProgram, Shader));
00388
00389
00390 sfGLCheck(glDeleteObjectARB(Shader));
00391
00392 return true;
00393 }
00394
00395
00399 void sfPostFX::InitVideoResources()
00400 {
00401
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
00409 if (myShaderProgram)
00410 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00411
00412
00413 myShaderProgram = glCreateProgramObjectARB();
00414
00415
00416 static const std::string VertexShader =
00417 "void main()"
00418 "{"
00419 " gl_TexCoord[0] = gl_MultiTexCoord0;"
00420 " gl_Position = ftransform();"
00421 "}";
00422
00423
00424 if (!CreateAndAttachShader(VertexShader, GL_VERTEX_SHADER_ARB))
00425 {
00426 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00427 myShaderProgram = 0;
00428 return;
00429 }
00430
00431
00432 if (!CreateAndAttachShader(myFragmentShader, GL_FRAGMENT_SHADER_ARB))
00433 {
00434 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00435 myShaderProgram = 0;
00436 return;
00437 }
00438
00439
00440 sfGLCheck(glLinkProgramARB(myShaderProgram));
00441
00442
00443 int Success;
00444 sfGLCheck(glGetObjectParameterivARB(myShaderProgram, GL_OBJECT_LINK_STATUS_ARB, &Success));
00445 if (Success == GL_FALSE)
00446 {
00447
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
00464 if (myShaderProgram)
00465 {
00466 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00467 myShaderProgram = 0;
00468 }
00469 }