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/Graphics/PostFX.hpp>
00029 #include <SFML/Graphics/GraphicsDevice.hpp>
00030 #include <SFML/Graphics/OpenGL.hpp>
00031 #include <SFML/Graphics/RenderWindow.hpp>
00032 #include <SFML/Window/OpenGLCaps.hpp>
00033 #include <fstream>
00034 #include <iostream>
00035 #include <set>
00036 #include <sstream>
00037
00038
00042 sfPostFX::sfPostFX() :
00043 myShaderProgram(0)
00044 {
00045
00046 }
00047
00048
00052 sfPostFX::sfPostFX(const std::string& Filename) :
00053 myShaderProgram(0)
00054 {
00055 LoadFromFile(Filename);
00056 }
00057
00058
00062 sfPostFX::sfPostFX(const sfPostFX& Copy) :
00063 myShaderProgram (0),
00064 mySamplers (Copy.mySamplers),
00065 myFragmentShader(Copy.myFragmentShader),
00066 myFrameBuffer (Copy.myFrameBuffer)
00067 {
00068
00069 if (Copy.myShaderProgram)
00070 CreateProgram();
00071 }
00072
00073
00077 sfPostFX::~sfPostFX()
00078 {
00079 DestroyVideoResources();
00080 }
00081
00082
00086 bool sfPostFX::LoadFromFile(const std::string& Filename)
00087 {
00088
00089 myFragmentShader = PreprocessEffect(Filename);
00090
00091
00092 CreateProgram();
00093
00094 return myShaderProgram != 0;
00095 }
00096
00097
00101 void sfPostFX::SetParameter(const std::string& Name, float X)
00102 {
00103 if (myShaderProgram)
00104 {
00105
00106 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00107
00108
00109 GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00110 if (Location != -1)
00111 sfGLCheck(glUniform1fARB(Location, X));
00112 else
00113 std::cerr << "Parameter \"" << Name << "\" not found in effect" << std::endl;
00114
00115
00116 sfGLCheck(glUseProgramObjectARB(0));
00117 }
00118 }
00119
00120
00124 void sfPostFX::SetParameter(const std::string& Name, float X, float Y)
00125 {
00126 if (myShaderProgram)
00127 {
00128
00129 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00130
00131
00132 GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00133 if (Location != -1)
00134 sfGLCheck(glUniform2fARB(Location, X, Y));
00135 else
00136 std::cerr << "Parameter \"" << Name << "\" not found in effect" << std::endl;
00137
00138
00139 sfGLCheck(glUseProgramObjectARB(0));
00140 }
00141 }
00142
00143
00147 void sfPostFX::SetParameter(const std::string& Name, float X, float Y, float Z)
00148 {
00149 if (myShaderProgram)
00150 {
00151
00152 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00153
00154
00155 GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00156 if (Location != -1)
00157 sfGLCheck(glUniform3fARB(Location, X, Y, Z));
00158 else
00159 std::cerr << "Parameter \"" << Name << "\" not found in effect" << std::endl;
00160
00161
00162 sfGLCheck(glUseProgramObjectARB(0));
00163 }
00164 }
00165
00166
00170 void sfPostFX::SetParameter(const std::string& Name, float X, float Y, float Z, float W)
00171 {
00172 if (myShaderProgram)
00173 {
00174
00175 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00176
00177
00178 GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
00179 if (Location != -1)
00180 sfGLCheck(glUniform4fARB(Location, X, Y, Z, W));
00181 else
00182 std::cerr << "Parameter \"" << Name << "\" not found in effect" << std::endl;
00183
00184
00185 sfGLCheck(glUseProgramObjectARB(0));
00186 }
00187 }
00188
00189
00193 void sfPostFX::SetTexture(const std::string& Name, sfImage* Image)
00194 {
00195
00196 mySamplers[Name] = Image ? Image : &myFrameBuffer;
00197 }
00198
00199
00203 sfPostFX& sfPostFX::operator =(const sfPostFX& Other)
00204 {
00205 sfPostFX Temp(Other);
00206
00207 std::swap(myShaderProgram, Temp.myShaderProgram);
00208 std::swap(mySamplers, Temp.mySamplers);
00209 std::swap(myFragmentShader, Temp.myFragmentShader);
00210 std::swap(myFrameBuffer, Temp.myFrameBuffer);
00211
00212 return *this;
00213 }
00214
00215
00219 bool sfPostFX::CanUsePostFX()
00220 {
00221 return sfOpenGLCaps::CheckExtension("GL_ARB_shading_language_100") &&
00222 sfOpenGLCaps::CheckExtension("GL_ARB_shader_objects") &&
00223 sfOpenGLCaps::CheckExtension("GL_ARB_vertex_shader") &&
00224 sfOpenGLCaps::CheckExtension("GL_ARB_fragment_shader");
00225 }
00226
00227
00231 void sfPostFX::Render(sfRenderWindow& Window)
00232 {
00233
00234 if (!myShaderProgram)
00235 return;
00236
00237
00238 if ((Window.GetWidth() != myFrameBuffer.GetWidth()) || (Window.GetHeight() != myFrameBuffer.GetHeight()))
00239 {
00240 myFrameBuffer.Resize(Window.GetWidth(), Window.GetHeight());
00241 myFrameBuffer.SetSmooth(false);
00242 myFrameBuffer.SetRepeat(false);
00243 }
00244
00245
00246 myFrameBuffer.Bind();
00247 sfGLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, myFrameBuffer.GetWidth(), myFrameBuffer.GetHeight()));
00248
00249
00250 sfGLCheck(glUseProgramObjectARB(myShaderProgram));
00251
00252
00253 int Unit = 0;
00254 for (std::map<std::string, sfImage*>::iterator i = mySamplers.begin(); i != mySamplers.end(); ++i)
00255 {
00256
00257 if (Unit >= sfOpenGLCaps::GetMaxTextureUnits())
00258 {
00259
00260 std::cerr << "Impossible to use texture \"" << i->first << "\" for post-effect : all available texture units are used" << std::endl;
00261 break;
00262 }
00263
00264
00265 int Location = glGetUniformLocationARB(myShaderProgram, i->first.c_str());
00266 if (Location != -1)
00267 {
00268
00269 sfGLCheck(glUniform1iARB(Location, Unit));
00270 sfGLCheck(glActiveTextureARB(GL_TEXTURE0_ARB + Unit));
00271 i->second->Bind();
00272 Unit++;
00273 }
00274 else
00275 {
00276
00277 std::cerr << "Texture \"" << i->first << "\" not found in effect" << std::endl;
00278 }
00279 }
00280
00281
00282 sfIntRect FrameBufferRect(0, 0, myFrameBuffer.GetWidth(), myFrameBuffer.GetHeight());
00283 sfFloatRect TexCoords = myFrameBuffer.GetTexCoords(FrameBufferRect);
00284
00285
00286 sfFloatRect Screen = Window.GetViewRect();
00287 glBegin(GL_QUADS);
00288 glTexCoord2f(TexCoords.Left, TexCoords.Top); glVertex2f(Screen.Left, Screen.Bottom);
00289 glTexCoord2f(TexCoords.Left, TexCoords.Bottom); glVertex2f(Screen.Left, Screen.Top);
00290 glTexCoord2f(TexCoords.Right, TexCoords.Bottom); glVertex2f(Screen.Right, Screen.Top);
00291 glTexCoord2f(TexCoords.Right, TexCoords.Top); glVertex2f(Screen.Right, Screen.Bottom);
00292 glEnd();
00293
00294
00295 sfGLCheck(glUseProgramObjectARB(0));
00296
00297
00298 for (int i = 0; i < Unit; ++i)
00299 {
00300 sfGLCheck(glActiveTextureARB(GL_TEXTURE0_ARB + i));
00301 sfGLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00302 }
00303 sfGLCheck(glActiveTextureARB(GL_TEXTURE0_ARB));
00304 }
00305
00306
00311 std::string sfPostFX::PreprocessEffect(const std::string& Filename)
00312 {
00313
00314 std::ifstream File(Filename.c_str());
00315 if (!File)
00316 {
00317 std::cerr << "Failed to open effect file \"" << Filename << "\"" << std::endl;
00318 return "";
00319 }
00320
00321
00322 std::set<std::string> myTextures;
00323 std::string Out = "";
00324
00325
00326 std::string Line;
00327 while (std::getline(File, Line) && (Line.substr(0, 6) != "effect"))
00328 {
00329
00330 if (!Line.empty() && (Line[Line.size() - 1] == '\r'))
00331 Line.erase(Line.size() - 1);
00332
00333
00334 if (Line == "")
00335 continue;
00336
00337
00338 std::string Type, Name;
00339 std::istringstream iss(Line);
00340 if (!(iss >> Type >> Name))
00341 {
00342 std::cerr << "Effect \"" << Filename << "\" : invalid declaration (should be \"[type][name]\")" << std::endl
00343 << "> " << Line << std::endl;
00344 return "";
00345 }
00346
00347 if (Type == "texture")
00348 {
00349
00350 if (myTextures.find(Name) != myTextures.end())
00351 {
00352 std::cerr << "Effect \"" << Filename << "\" : texture \"" << Name << "\" already exists" << std::endl;
00353 return "";
00354 }
00355
00356 Out += "uniform sampler2D " + Name + ";\n";
00357 myTextures.insert(Name);
00358 }
00359 else
00360 {
00361
00362 Out += "uniform " + Type + " " + Name + ";\n";
00363 }
00364 }
00365
00366
00367 Out += "void main()\n";
00368 while (std::getline(File, Line))
00369 {
00370
00371 for (std::set<std::string>::const_iterator i = myTextures.begin(); i != myTextures.end(); ++i)
00372 {
00373 std::string::size_type Pos = Line.find(*i);
00374 if (Pos != std::string::npos)
00375 Line.replace(Pos, i->size() + 1, "texture2D(" + *i + ", ");
00376 }
00377
00378
00379 for (std::string::size_type Pos = Line.find("_in"); Pos != std::string::npos; Pos = Line.find("_in"))
00380 Line.replace(Pos, 3, "gl_TexCoord[0].xy");
00381
00382
00383 for (std::string::size_type Pos = Line.find("_out"); Pos != std::string::npos; Pos = Line.find("_out"))
00384 Line.replace(Pos, 4, "gl_FragColor");
00385
00386
00387 Out += Line + "\n";
00388 }
00389
00390 return Out;
00391 }
00392
00393
00397 bool sfPostFX::CreateAndAttachShader(const std::string& Source, unsigned int ShaderType)
00398 {
00399
00400 GLhandleARB Shader = glCreateShaderObjectARB(ShaderType);
00401
00402
00403 int Length = static_cast<int>(Source.size());
00404 const char* Data = Source.data();
00405 sfGLCheck(glShaderSourceARB(Shader, 1, &Data, &Length));
00406
00407
00408 sfGLCheck(glCompileShaderARB(Shader));
00409
00410
00411 int Success;
00412 sfGLCheck(glGetObjectParameterivARB(Shader, GL_OBJECT_COMPILE_STATUS_ARB, &Success));
00413 if (Success == GL_FALSE)
00414 {
00415
00416 char CompileLog[1024];
00417 sfGLCheck(glGetInfoLogARB(Shader, sizeof(CompileLog), 0, CompileLog));
00418 std::cerr << "Failed to compile effect :" << std::endl
00419 << CompileLog << std::endl;
00420 sfGLCheck(glDeleteObjectARB(Shader));
00421 return false;
00422 }
00423
00424
00425 sfGLCheck(glAttachObjectARB(myShaderProgram, Shader));
00426
00427
00428 sfGLCheck(glDeleteObjectARB(Shader));
00429
00430 return true;
00431 }
00432
00433
00437 void sfPostFX::CreateProgram()
00438 {
00439
00440 if (!CanUsePostFX())
00441 {
00442 std::cerr << "Failed to create a sfPostFX : your system doesn't support effects" << std::endl;
00443 return;
00444 }
00445
00446
00447 if (myShaderProgram)
00448 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00449
00450
00451 myShaderProgram = glCreateProgramObjectARB();
00452
00453
00454 static const std::string VertexShader =
00455 "void main()"
00456 "{"
00457 " gl_TexCoord[0] = gl_MultiTexCoord0;"
00458 " gl_Position = ftransform();"
00459 "}";
00460
00461
00462 if (!CreateAndAttachShader(VertexShader, GL_VERTEX_SHADER_ARB))
00463 {
00464 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00465 myShaderProgram = 0;
00466 return;
00467 }
00468
00469
00470 if (!CreateAndAttachShader(myFragmentShader, GL_FRAGMENT_SHADER_ARB))
00471 {
00472 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00473 myShaderProgram = 0;
00474 return;
00475 }
00476
00477
00478 sfGLCheck(glLinkProgramARB(myShaderProgram));
00479
00480
00481 int Success;
00482 sfGLCheck(glGetObjectParameterivARB(myShaderProgram, GL_OBJECT_LINK_STATUS_ARB, &Success));
00483 if (Success == GL_FALSE)
00484 {
00485
00486 char LinkLog[1024];
00487 sfGLCheck(glGetInfoLogARB(myShaderProgram, sizeof(LinkLog), 0, LinkLog));
00488 std::cerr << "Failed to link effect :" << std::endl
00489 << LinkLog << std::endl;
00490 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00491 myShaderProgram = 0;
00492 }
00493 }
00494
00495
00499 void sfPostFX::DestroyVideoResources()
00500 {
00501
00502 if (myShaderProgram)
00503 {
00504 sfGLCheck(glDeleteObjectARB(myShaderProgram));
00505 myShaderProgram = 0;
00506 }
00507 }