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