PostFX.cpp
1
2//
3// SFML - Simple and Fast Multimedia Library
4// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
24
25
27// Headers
29#include <SFML/Graphics/PostFX.hpp>
30#include <SFML/Graphics/RenderWindow.hpp>
31#include <SFML/Graphics/GraphicsContext.hpp>
32#include <fstream>
33#include <iostream>
34#include <set>
35#include <sstream>
36
37
38namespace sf
39{
44myShaderProgram(0)
45{
46 // No filtering on frame buffer
47 myFrameBuffer.SetSmooth(false);
48}
49
50
54PostFX::PostFX(const PostFX& Copy) :
55Drawable (Copy),
56myShaderProgram (0),
57myTextures (Copy.myTextures),
58myFragmentShader(Copy.myFragmentShader),
59myFrameBuffer (Copy.myFrameBuffer)
60{
61 // No filtering on frame buffer
62 myFrameBuffer.SetSmooth(false);
63
64 // Create the shaders and the program
65 if (Copy.myShaderProgram)
66 CreateProgram();
67}
68
69
74{
75 // Destroy effect program
76 if (myShaderProgram)
77 {
78 // Make sure we have a valid context
79 priv::GraphicsContext Ctx;
80
81 GLCheck(glDeleteObjectARB(myShaderProgram));
82 }
83}
84
85
89bool PostFX::LoadFromFile(const std::string& Filename)
90{
91 // Open the file
92 std::ifstream File(Filename.c_str());
93 if (!File)
94 {
95 std::cerr << "Failed to open effect file \"" << Filename << "\"" << std::endl;
96 return false;
97 }
98
99 // Apply the preprocessing pass to the fragment shader code
100 myFragmentShader = PreprocessEffect(File);
101
102 // Create the shaders and the program
103 CreateProgram();
104
105 return myShaderProgram != 0;
106}
107
108
112bool PostFX::LoadFromMemory(const std::string& Effect)
113{
114 // Open a stream and copy the effect code
115 std::istringstream Stream(Effect.c_str());
116
117 // Apply the preprocessing pass to the fragment shader code
118 myFragmentShader = PreprocessEffect(Stream);
119
120 // Create the shaders and the program
121 CreateProgram();
122
123 return myShaderProgram != 0;
124}
125
126
130void PostFX::SetParameter(const std::string& Name, float X)
131{
132 if (myShaderProgram)
133 {
134 // Enable program
135 GLCheck(glUseProgramObjectARB(myShaderProgram));
136
137 // Get parameter location and assign it new values
138 GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
139 if (Location != -1)
140 GLCheck(glUniform1fARB(Location, X));
141 else
142 std::cerr << "Parameter \"" << Name << "\" not found in post-effect" << std::endl;
143
144 // Disable program
145 GLCheck(glUseProgramObjectARB(0));
146 }
147}
148
149
153void PostFX::SetParameter(const std::string& Name, float X, float Y)
154{
155 if (myShaderProgram)
156 {
157 // Enable program
158 GLCheck(glUseProgramObjectARB(myShaderProgram));
159
160 // Get parameter location and assign it new values
161 GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
162 if (Location != -1)
163 GLCheck(glUniform2fARB(Location, X, Y));
164 else
165 std::cerr << "Parameter \"" << Name << "\" not found in post-effect" << std::endl;
166
167 // Disable program
168 GLCheck(glUseProgramObjectARB(0));
169 }
170}
171
172
176void PostFX::SetParameter(const std::string& Name, float X, float Y, float Z)
177{
178 if (myShaderProgram)
179 {
180 // Enable program
181 GLCheck(glUseProgramObjectARB(myShaderProgram));
182
183 // Get parameter location and assign it new values
184 GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
185 if (Location != -1)
186 GLCheck(glUniform3fARB(Location, X, Y, Z));
187 else
188 std::cerr << "Parameter \"" << Name << "\" not found in post-effect" << std::endl;
189
190 // Disable program
191 GLCheck(glUseProgramObjectARB(0));
192 }
193}
194
195
199void PostFX::SetParameter(const std::string& Name, float X, float Y, float Z, float W)
200{
201 if (myShaderProgram)
202 {
203 // Enable program
204 GLCheck(glUseProgramObjectARB(myShaderProgram));
205
206 // Get parameter location and assign it new values
207 GLint Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
208 if (Location != -1)
209 GLCheck(glUniform4fARB(Location, X, Y, Z, W));
210 else
211 std::cerr << "Parameter \"" << Name << "\" not found in post-effect" << std::endl;
212
213 // Disable program
214 GLCheck(glUseProgramObjectARB(0));
215 }
216}
217
218
222void PostFX::SetTexture(const std::string& Name, Image* Texture)
223{
224 // Check that the current texture unit is available
225 GLint MaxUnits;
226 GLCheck(glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB, &MaxUnits));
227 if (myTextures.size() >= static_cast<std::size_t>(MaxUnits))
228 {
229 std::cerr << "Impossible to use texture \"" << Name << "\" for post-effect : all available texture units are used" << std::endl;
230 return;
231 }
232
233 // Make sure the given name is a valid variable in the effect
234 int Location = glGetUniformLocationARB(myShaderProgram, Name.c_str());
235 if (Location == -1)
236 {
237 std::cerr << "Texture \"" << Name << "\" not found in post-effect" << std::endl;
238 return;
239 }
240
241 // Store the texture for later use
242 myTextures[Name] = Texture ? Texture : &myFrameBuffer;
243}
244
245
250{
251 PostFX Temp(Other);
252
253 std::swap(myShaderProgram, Temp.myShaderProgram);
254 std::swap(myTextures, Temp.myTextures);
255 std::swap(myFragmentShader, Temp.myFragmentShader);
256 std::swap(myFrameBuffer, Temp.myFrameBuffer);
257
258 return *this;
259}
260
261
266{
267 // Make sure we have a valid context
268 priv::GraphicsContext Ctx;
269
270 return glewIsSupported("GL_ARB_shading_language_100") != 0 &&
271 glewIsSupported("GL_ARB_shader_objects") != 0 &&
272 glewIsSupported("GL_ARB_vertex_shader") != 0 &&
273 glewIsSupported("GL_ARB_fragment_shader") != 0;
274}
275
276
280void PostFX::Render(RenderTarget& Target) const
281{
282 // Check that we have a valid program
283 if (!myShaderProgram)
284 return;
285
286 // Copy the current framebuffer pixels to our frame buffer texture
287 // The ugly cast is temporary until PostFx are rewritten :)
288 myFrameBuffer.CopyScreen((RenderWindow&)Target);
289
290 // Enable program
291 GLCheck(glUseProgramObjectARB(myShaderProgram));
292
293 // Bind textures
294 TextureTable::const_iterator ItTex = myTextures.begin();
295 for (std::size_t i = 0; i < myTextures.size(); ++i)
296 {
297 int Location = glGetUniformLocationARB(myShaderProgram, ItTex->first.c_str());
298 GLCheck(glUniform1iARB(Location, static_cast<GLint>(i)));
299 GLCheck(glActiveTextureARB(static_cast<GLenum>(GL_TEXTURE0_ARB + i)));
300 ItTex->second->Bind();
301 ItTex++;
302 }
303
304 // Compute the texture coordinates (in case the texture is larger than the screen, or flipped)
305 IntRect FrameBufferRect(0, 0, myFrameBuffer.GetWidth(), myFrameBuffer.GetHeight());
306 FloatRect TexCoords = myFrameBuffer.GetTexCoords(FrameBufferRect);
307
308 // Render a fullscreen quad using the effect on our framebuffer
309 FloatRect Screen = Target.GetView().GetRect();
310 glBegin(GL_QUADS);
311 glTexCoord2f(TexCoords.Left, TexCoords.Top); glVertex2f(Screen.Left, Screen.Bottom);
312 glTexCoord2f(TexCoords.Right, TexCoords.Top); glVertex2f(Screen.Right, Screen.Bottom);
313 glTexCoord2f(TexCoords.Right, TexCoords.Bottom); glVertex2f(Screen.Right, Screen.Top);
314 glTexCoord2f(TexCoords.Left, TexCoords.Bottom); glVertex2f(Screen.Left, Screen.Top);
315 glEnd();
316
317 // Disable program
318 GLCheck(glUseProgramObjectARB(0));
319
320 // Disable texture units
321 for (std::size_t i = 0; i < myTextures.size(); ++i)
322 {
323 GLCheck(glActiveTextureARB(static_cast<GLenum>(GL_TEXTURE0_ARB + i)));
324 GLCheck(glBindTexture(GL_TEXTURE_2D, 0));
325 }
326 GLCheck(glActiveTextureARB(GL_TEXTURE0_ARB));
327}
328
329
334std::string PostFX::PreprocessEffect(std::istream& File)
335{
336 // Initialize output string
337 std::set<std::string> myTextures;
338 std::string Out = "";
339
340 // Variable declarations
341 std::string Line;
342 while (std::getline(File, Line) && (Line.substr(0, 6) != "effect"))
343 {
344 // Remove the ending '\r', if any
345 if (!Line.empty() && (Line[Line.size() - 1] == '\r'))
346 Line.erase(Line.size() - 1);
347
348 // Skip empty lines
349 if (Line == "")
350 continue;
351
352 // Extract variables type and name and convert them
353 std::string Type, Name;
354 std::istringstream iss(Line);
355 if (!(iss >> Type >> Name))
356 {
357 std::cerr << "Post-effect error : invalid declaration (should be \"[type][name]\")" << std::endl
358 << "> " << Line << std::endl;
359 return "";
360 }
361
362 if (Type == "texture")
363 {
364 // Textures need some checking and conversion
365 if (myTextures.find(Name) != myTextures.end())
366 {
367 std::cerr << "Post-effect error : texture \"" << Name << "\" already exists" << std::endl;
368 return "";
369 }
370
371 Out += "uniform sampler2D " + Name + ";\n";
372 myTextures.insert(Name);
373 }
374 else
375 {
376 // Other types are just copied to output with "uniform" prefix
377 Out += "uniform " + Type + " " + Name + ";\n";
378 }
379 }
380
381 // Effect code
382 Out += "void main()\n";
383 while (std::getline(File, Line))
384 {
385 // Replace any texture lookup "T(" by "texture2D(T, "
386 for (std::set<std::string>::const_iterator i = myTextures.begin(); i != myTextures.end(); ++i)
387 {
388 std::string::size_type Pos = Line.find(*i);
389 if (Pos != std::string::npos)
390 Line.replace(Pos, i->size() + 1, "texture2D(" + *i + ", ");
391 }
392
393 // Replace "_in" by "gl_TexCoord[0].xy"
394 for (std::string::size_type Pos = Line.find("_in"); Pos != std::string::npos; Pos = Line.find("_in"))
395 Line.replace(Pos, 3, "gl_TexCoord[0].xy");
396
397 // Replace "_out" by "gl_FragColor"
398 for (std::string::size_type Pos = Line.find("_out"); Pos != std::string::npos; Pos = Line.find("_out"))
399 Line.replace(Pos, 4, "gl_FragColor");
400
401 // Write modified line to output string
402 Out += Line + "\n";
403 }
404
405 return Out;
406}
407
408
412void PostFX::CreateProgram()
413{
414 // Make sure we have a valid context
415 priv::GraphicsContext Ctx;
416
417 // Check that we can use post-FX !
418 if (!CanUsePostFX())
419 {
420 std::cerr << "Failed to create a PostFX : your system doesn't support effects" << std::endl;
421 return;
422 }
423
424 // Destroy effect program if it was already created
425 if (myShaderProgram)
426 GLCheck(glDeleteObjectARB(myShaderProgram));
427
428 // Define vertex shader source (we provide it directly as it doesn't have to change)
429 static const std::string VertexShaderSrc =
430 "void main()"
431 "{"
432 " gl_TexCoord[0] = gl_MultiTexCoord0;"
433 " gl_Position = ftransform();"
434 "}";
435
436 // Create the program
437 myShaderProgram = glCreateProgramObjectARB();
438
439 // Create the shaders
440 GLhandleARB VertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
441 GLhandleARB FragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
442
443 // Compile them
444 const char* VertexSrc = VertexShaderSrc.c_str();
445 const char* FragmentSrc = myFragmentShader.c_str();
446 GLCheck(glShaderSourceARB(VertexShader, 1, &VertexSrc, NULL));
447 GLCheck(glShaderSourceARB(FragmentShader, 1, &FragmentSrc, NULL));
448 GLCheck(glCompileShaderARB(VertexShader));
449 GLCheck(glCompileShaderARB(FragmentShader));
450
451 // Check the compile logs
452 GLint Success;
453 GLCheck(glGetObjectParameterivARB(VertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &Success));
454 if (Success == GL_FALSE)
455 {
456 char CompileLog[1024];
457 GLCheck(glGetInfoLogARB(VertexShader, sizeof(CompileLog), 0, CompileLog));
458 std::cerr << "Failed to compile post-effect :" << std::endl
459 << CompileLog << std::endl;
460 GLCheck(glDeleteObjectARB(VertexShader));
461 GLCheck(glDeleteObjectARB(FragmentShader));
462 GLCheck(glDeleteObjectARB(myShaderProgram));
463 myShaderProgram = 0;
464 return;
465 }
466 GLCheck(glGetObjectParameterivARB(FragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &Success));
467 if (Success == GL_FALSE)
468 {
469 char CompileLog[1024];
470 GLCheck(glGetInfoLogARB(FragmentShader, sizeof(CompileLog), 0, CompileLog));
471 std::cerr << "Failed to compile post-effect :" << std::endl
472 << CompileLog << std::endl;
473 GLCheck(glDeleteObjectARB(VertexShader));
474 GLCheck(glDeleteObjectARB(FragmentShader));
475 GLCheck(glDeleteObjectARB(myShaderProgram));
476 myShaderProgram = 0;
477 return;
478 }
479
480 // Attach the shaders to the program
481 GLCheck(glAttachObjectARB(myShaderProgram, VertexShader));
482 GLCheck(glAttachObjectARB(myShaderProgram, FragmentShader));
483
484 // We can now delete the shaders
485 GLCheck(glDeleteObjectARB(VertexShader));
486 GLCheck(glDeleteObjectARB(FragmentShader));
487
488 // Link the program
489 GLCheck(glLinkProgramARB(myShaderProgram));
490
491 // Get link log
492 GLCheck(glGetObjectParameterivARB(myShaderProgram, GL_OBJECT_LINK_STATUS_ARB, &Success));
493 if (Success == GL_FALSE)
494 {
495 // Oops... link errors
496 char LinkLog[1024];
497 GLCheck(glGetInfoLogARB(myShaderProgram, sizeof(LinkLog), 0, LinkLog));
498 std::cerr << "Failed to link post-effect :" << std::endl
499 << LinkLog << std::endl;
500 GLCheck(glDeleteObjectARB(myShaderProgram));
501 myShaderProgram = 0;
502 return;
503 }
504}
505
506} // namespace sf
Drawable(const Vector2f &Position=Vector2f(0, 0), const Vector2f &Scale=Vector2f(1, 1), float Rotation=0.f, const Color &Col=Color(255, 255, 255, 255))
Default constructor.
Definition Drawable.cpp:39
Image is the low-level class for loading and manipulating images.
Definition Image.hpp:47
bool LoadFromFile(const std::string &Filename)
Load the effect from a file.
Definition PostFX.cpp:89
PostFX()
Default constructor.
Definition PostFX.cpp:43
void SetTexture(const std::string &Name, Image *Texture)
Set a texture parameter.
Definition PostFX.cpp:222
void SetParameter(const std::string &Name, float X)
Change a parameter of the effect (1 float).
Definition PostFX.cpp:130
bool LoadFromMemory(const std::string &Effect)
Load the effect from a text in memory.
Definition PostFX.cpp:112
static bool CanUsePostFX()
Tell whether or not the system supports post-effects.
Definition PostFX.cpp:265
~PostFX()
Destructor.
Definition PostFX.cpp:73
virtual void Render(RenderTarget &Target) const
/see Drawable::Render
Definition PostFX.cpp:280
PostFX & operator=(const PostFX &Other)
Assignment operator.
Definition PostFX.cpp:249
T Top
Top coordinate of the rectangle.
Definition Rect.hpp:113
T Right
Right coordinate of the rectangle.
Definition Rect.hpp:114
T Left
Left coordinate of the rectangle.
Definition Rect.hpp:112
T Bottom
Bottom coordinate of the rectangle.
Definition Rect.hpp:115
const View & GetView() const
Get the current view.
Simple wrapper for sf::Window that allows easy 2D rendering.
const sf::FloatRect & GetRect() const
Get the bounding rectangle of the view.
Definition View.cpp:126