From 9fd9a6cc4a243543b41408b911607aff7d233241 Mon Sep 17 00:00:00 2001
From: DarkGod <darkgod@net-core.org>
Date: Wed, 12 Feb 2014 01:19:43 +0100
Subject: [PATCH] All fullscreen effects can be enabled at once now
 (underwater, blur, wobbling, ...)

---
 game/modules/tome/class/Game.lua              |   9 +-
 game/modules/tome/class/Player.lua            |  25 +--
 .../tome/data/gfx/shaders/main_fbo.frag       |  88 ----------
 .../tome/data/gfx/shaders/main_fbo/blur.frag  |  25 +++
 .../tome/data/gfx/shaders/main_fbo/blur.lua   |  28 +++
 .../data/gfx/shaders/main_fbo/motionblur.frag |  69 ++++++++
 .../data/gfx/shaders/main_fbo/motionblur.lua  |  28 +++
 .../data/gfx/shaders/main_fbo/underwater.frag |  84 +++++++++
 .../data/gfx/shaders/main_fbo/underwater.lua  |  28 +++
 .../data/gfx/shaders/main_fbo/wobbling.frag   | 165 ++++++++++++++++++
 .../data/gfx/shaders/main_fbo/wobbling.lua    |  29 +++
 src/core_lua.c                                |  90 ++++++++++
 12 files changed, 567 insertions(+), 101 deletions(-)
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/blur.frag
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/blur.lua
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/motionblur.frag
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/motionblur.lua
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/underwater.frag
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/underwater.lua
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/wobbling.frag
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/wobbling.lua

diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 7383250a01..c6e2337a50 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -514,6 +514,13 @@ function _M:createFBOs()
 	self.fbo = core.display.newFBO(Map.viewport.width, Map.viewport.height)
 	if self.fbo then
 		self.fbo_shader = Shader.new("main_fbo")
+		self.posteffects = {
+			wobbling = Shader.new("main_fbo/wobbling"),
+			underwater = Shader.new("main_fbo/underwater"),
+			motionblur = Shader.new("main_fbo/motionblur"),
+			blur = Shader.new("main_fbo/blur"),
+		}
+		self.posteffects_use = { self.fbo_shader.shad }
 		if not self.fbo_shader.shad then self.fbo = nil self.fbo_shader = nil end 
 		self.fbo2 = core.display.newFBO(Map.viewport.width, Map.viewport.height)
 	end
@@ -1392,7 +1399,7 @@ function _M:displayMap(nb_keyframes)
 			self.fbo2:use(false, self.full_fbo)
 
 			_2DNoise:bind(1, false)
-			self.fbo2:toScreen(map.display_x, map.display_y, map.viewport.width, map.viewport.height, self.fbo_shader.shad)
+			self.fbo2:postEffects(self.fbo, self.full_fbo, map.display_x, map.display_y, map.viewport.width, map.viewport.height, unpack(self.posteffects_use))
 			if self.target then self.target:display(nil, nil, self.full_fbo, nb_keyframes) end
 
 		-- Basic display; no FBOs
diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua
index ad6259aaf3..b29488f322 100644
--- a/game/modules/tome/class/Player.lua
+++ b/game/modules/tome/class/Player.lua
@@ -374,6 +374,9 @@ end
 --- Funky shader stuff
 function _M:updateMainShader()
 	if game.fbo_shader then
+		local effects = {}
+		local pf = game.posteffects
+
 		-- Set shader HP warning
 		if self.life ~= self.shader_old_life then
 			if self.life < self.max_life / 2 then game.fbo_shader:setUniform("hp_warning", 1 - (self.life / self.max_life))
@@ -408,28 +411,26 @@ function _M:updateMainShader()
 		end
 
 		-- Blur shader
-		if self:attr("confused") and self.confused >= 1 then game.fbo_shader:setUniform("blur", 2)
---		elseif game:hasDialogUp() then game.fbo_shader:setUniform("blur", 3)
-		else game.fbo_shader:setUniform("blur", 0) -- Disable
+		if self:attr("confused") and self.confused >= 1 then pf.blur.shad:uniBlur(2) effects[pf.blur.shad] = true
 		end
 
 		-- Moving Blur shader
-		if self:attr("invisible") then game.fbo_shader:setUniform("motionblur", 3)
-		elseif self:attr("lightning_speed") then game.fbo_shader:setUniform("motionblur", 2)
-		elseif game.level and game.level.data and game.level.data.motionblur then game.fbo_shader:setUniform("motionblur", game.level.data.motionblur)
-		else game.fbo_shader:setUniform("motionblur", 0) -- Disable
+		if self:attr("invisible") then pf.motionblur.shad:uniMotionblur(3) effects[pf.motionblur.shad] = true
+		elseif self:attr("lightning_speed") then pf.motionblur.shad:uniMotionblur(2) effects[pf.motionblur.shad] = true
+		elseif game.level and game.level.data and game.level.data.motionblur then pf.motionblur.shad:uniMotionblur(game.level.data.motionblur) effects[pf.motionblur.shad] = true
 		end
 
 		-- Underwater shader
-		if game.level and game.level.data and game.level.data.underwater then game.fbo_shader:setUniform("underwater", 1)
-		else game.fbo_shader:setUniform("underwater", 0) -- Disable
+		if game.level and game.level.data and game.level.data.underwater then effects[pf.underwater.shad] = true
 		end
 
 		-- Wobbling shader
-		if self:attr("stunned") and self.stunned >= 1 then game.fbo_shader:setUniform("wobbling", 1)
-		elseif self:attr("dazed") and self.dazed >= 1 then game.fbo_shader:setUniform("wobbling", 0.7)
-		else game.fbo_shader:setUniform("wobbling", 0) -- Disable
+		if self:attr("stunned") and self.stunned >= 1 then pf.wobbling.shad:uniWobbling(1) effects[pf.wobbling.shad] = true
+		elseif self:attr("dazed") and self.dazed >= 1 then pf.wobbling.shad:uniWobbling(0.7) effects[pf.wobbling.shad] = true
 		end
+
+		game.posteffects_use = table.keys(effects)
+		game.posteffects_use[#game.posteffects_use+1] = game.fbo_shader.shad
 	end
 end
 
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo.frag b/game/modules/tome/data/gfx/shaders/main_fbo.frag
index 4eb1a25d3e..cb595315d4 100644
--- a/game/modules/tome/data/gfx/shaders/main_fbo.frag
+++ b/game/modules/tome/data/gfx/shaders/main_fbo.frag
@@ -271,30 +271,6 @@ void main(void)
 		sample /= (blur*2.0) * (blur*2.0);
 		gl_FragColor = sample;
 	}
-	else if (underwater > 0.0)
-	{
-		vec2 p = (vec2(gl_FragCoord.x - mapCoord.x, texSize.y - gl_FragCoord.y - mapCoord.y)) / texSize.xy, c1 = p, c2 = p;
-		float cc1 = col(c1);
-
-		c2.x += texSize.x/delta;
-		float dx = emboss*(cc1-col(c2))/delta;
-
-		c2.x = p.x;
-		c2.y += texSize.y/delta;
-		float dy = emboss*(cc1-col(c2))/delta;
-
-		c1.x += dx*2.;
-		c1.y = -(c1.y+dy*2.);
-
-		float alpha = 1.+dot(dx,dy)*intence;
-			
-		float ddx = dx - reflectionCutOff;
-		float ddy = dy - reflectionCutOff;
-		if (ddx > 0. && ddy > 0.) alpha = pow(alpha, ddx*ddy*reflectionIntence);
-			
-		vec4 col = texture2D(tex,c1)*(alpha);
-		gl_FragColor = col;
-	}
 
 	if (colorize.r > 0.0 || colorize.g > 0.0 || colorize.b > 0.0)
 	{
@@ -335,67 +311,3 @@ void main(void)
 		gl_FragColor = mix(gl_FragColor, solipsism_warning_color, dist);
 	}
 }
-
-/*uniform sampler2D tex;
-uniform vec2 texSize;
-int blursize = 5;
-
-void main(void)
-{
-	vec2 offset = 1.0/texSize;
-
-	// Center Pixel
-	vec4 sample = vec4(0.0,0.0,0.0,0.0);
-	float factor = ((float(blursize)*2.0)+1.0);
-	factor = factor*factor;
-
-	for(int i = -blursize; i <= blursize; i++)
-	{
-		for(int j = -blursize; j <= blursize; j++)
-		{
-			sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y)));
-		}
-	}
-	sample /= float((blursize*2) * (blursize*2));
-
-	float grey = sample.r*0.3+sample.g*0.59+sample.b*0.11;
-	vec3 color = vec3(1, 0, 0);
-	gl_FragColor = vec4(vec3(color*grey),1.0);
-}
-*/
-/*
-uniform sampler2D tex;
-uniform sampler3D noiseVol;
-uniform float tick;
-float do_blur = 3.0;
-uniform vec2 texSize;
-
-void main(void)
-{
-	if (do_blur > 0.0)
-	{
-		vec2 offset = 1.0/texSize;
-		offset.y += texture3D(noiseVol, vec3(gl_TexCoord[0].xy, tick/100000))/30;
-
-		// Center Pixel
-		vec4 sample = vec4(0.0,0.0,0.0,0.0);
-		float factor = ((float(do_blur)*2.0)+1.0);
-		factor = factor*factor;
-
-		for(int i = -do_blur; i <= do_blur; i++)
-		{
-			for(int j = -do_blur; j <= do_blur; j++)
-			{
-				sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y)));
-			}
-		}
-		sample /= float((do_blur*2) * (do_blur*2));
-
-		gl_FragColor = sample;
-	}
-
-	float grey = gl_FragColor.r*0.3+gl_FragColor.g*0.59+gl_FragColor.b*0.11;
-	vec3 color = vec3(1, 0, 0);
-	gl_FragColor = vec4(vec3(color*grey),1.0);
-}
-*/
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/blur.frag b/game/modules/tome/data/gfx/shaders/main_fbo/blur.frag
new file mode 100644
index 0000000000..492d77f4f3
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/blur.frag
@@ -0,0 +1,25 @@
+uniform float blur;
+uniform float tick;
+uniform vec2 texSize;
+uniform sampler2D tex;
+
+void main(void)
+{
+	int blursize = int(blur);
+	vec2 offset = 1.0/texSize;
+
+	// Center Pixel
+	vec4 sample = vec4(0.0,0.0,0.0,0.0);
+	float factor = ((float(blursize)*2.0)+1.0);
+	factor = factor*factor;
+
+	for(int i = -blursize; i <= blursize; i++)
+	{
+		for(int j = -blursize; j <= blursize; j++)
+		{
+			sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y)));
+		}
+	}
+	sample /= (blur*2.0) * (blur*2.0);
+	gl_FragColor = sample;
+}
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/blur.lua b/game/modules/tome/data/gfx/shaders/main_fbo/blur.lua
new file mode 100644
index 0000000000..08847b5d91
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/blur.lua
@@ -0,0 +1,28 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+return {
+	frag = "main_fbo/blur",
+	vert = nil,
+	args = {
+		tex = { texture = 0 },
+	},
+	clone = false,
+	permanent = true,
+}
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/motionblur.frag b/game/modules/tome/data/gfx/shaders/main_fbo/motionblur.frag
new file mode 100644
index 0000000000..508bb84284
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/motionblur.frag
@@ -0,0 +1,69 @@
+uniform float motionblur;
+uniform float tick;
+uniform sampler2D noisevol;
+uniform vec2 texSize;
+uniform sampler2D tex;
+uniform vec2 mapCoord;
+
+void main(void)
+{
+	gl_FragColor = texture2D(tex, gl_TexCoord[0].xy);
+
+	int blursize = int(motionblur);
+	vec2 offset = 0.8/texSize;
+
+	float fTime0_X = tick / 20000.0;
+	float coord = gl_TexCoord[0].x + gl_TexCoord[0].y * texSize[0];
+	float noisy1 = texture2D(noisevol,vec2(coord,fTime0_X)).r;
+	float noisy2 = texture2D(noisevol,vec2(coord/5.0,fTime0_X/1.5)).r;
+	float noisy3 = texture2D(noisevol,vec2(coord/7.0,fTime0_X/2.0)).r;
+	float noisy = (noisy1+noisy2+noisy3)/3.0;
+
+	// Center Pixel
+	vec4 sample = vec4(0.0,0.0,0.0,0.0);
+	float factor = ((float(blursize)*2.0)+1.0);
+	factor = factor*factor;
+
+	if (noisy < 0.25)
+	{
+		for(int i = -blursize; i <= 0; i++)
+		{
+			for(int j = -blursize; j <= 0; j++)
+			{
+				sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y)));
+			}
+		}
+	}
+	else if (noisy < 0.50)
+	{
+		for(int i = 0; i <= blursize; i++)
+		{
+			for(int j = 0; j <= blursize; j++)
+			{
+				sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y)));
+			}
+		}
+	}
+	else if (noisy < 0.75)
+	{
+		for(int i = 0; i <= blursize; i++)
+		{
+			for(int j = -blursize; j <= 0; j++)
+			{
+				sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y)));
+			}
+		}
+	}
+	else
+	{
+		for(int i = -blursize; i <= 0; i++)
+		{
+			for(int j = 0; j <= blursize; j++)
+			{
+				sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y)));
+			}
+		}
+	}
+	sample /= float((motionblur*1.5) * (motionblur*0.5));
+	gl_FragColor = sample;
+}
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/motionblur.lua b/game/modules/tome/data/gfx/shaders/main_fbo/motionblur.lua
new file mode 100644
index 0000000000..4ca6ca12c3
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/motionblur.lua
@@ -0,0 +1,28 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+return {
+	frag = "main_fbo/motionblur",
+	vert = nil,
+	args = {
+		tex = { texture = 0 },
+	},
+	clone = false,
+	permanent = true,
+}
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/underwater.frag b/game/modules/tome/data/gfx/shaders/main_fbo/underwater.frag
new file mode 100644
index 0000000000..ba413ab5f6
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/underwater.frag
@@ -0,0 +1,84 @@
+uniform float tick;
+uniform vec2 texSize;
+uniform sampler2D tex;
+
+// Simple Water shader. (c) Victor Korsun, bitekas@gmail.com; 2012.
+//
+// Attribution-ShareAlike CC License.
+
+#ifdef GL_ES
+precision highp float;
+#endif
+
+uniform vec2 mapCoord;
+
+const float PI = 3.1415926535897932;
+
+// play with these parameters to custimize the effect
+// ===================================================
+
+//speed
+const float speed = 0.05;
+const float speed_x = 0.3;
+const float speed_y = 0.3;
+
+// refraction
+const float emboss = 0.05;
+const float intensity = 0.4;
+const int steps = 6;
+const float frequency = 100.0;
+const int angle = 7; // better when a prime
+
+// reflection
+const float delta = 60.;
+const float intence = 700.;
+
+const float reflectionCutOff = 0.012;
+const float reflectionIntence = 200000.;
+
+// ===================================================
+
+float time = tick / 30000.0;
+
+float col(vec2 coord)
+{
+	float delta_theta = 2.0 * PI / float(angle);
+	float col = 0.0;
+	float theta = 0.0;
+	for (int i = 0; i < steps; i++)
+	{
+		vec2 adjc = coord;
+		theta = delta_theta*float(i);
+		adjc.x += cos(theta)*time*speed + time * speed_x;
+		adjc.y -= sin(theta)*time*speed - time * speed_y;
+		col = col + cos( (adjc.x*cos(theta) - adjc.y*sin(theta))*frequency)*intensity;
+	}
+
+	return cos(col);
+}
+
+void main(void)
+{
+	gl_FragColor = texture2D(tex, gl_TexCoord[0].xy);
+	vec2 p = (vec2(gl_FragCoord.x - mapCoord.x, texSize.y - gl_FragCoord.y - mapCoord.y)) / texSize.xy, c1 = p, c2 = p;
+	float cc1 = col(c1);
+
+	c2.x += texSize.x/delta;
+	float dx = emboss*(cc1-col(c2))/delta;
+
+	c2.x = p.x;
+	c2.y += texSize.y/delta;
+	float dy = emboss*(cc1-col(c2))/delta;
+
+	c1.x += dx*2.;
+	c1.y = -(c1.y+dy*2.);
+
+	float alpha = 1.+dot(dx,dy)*intence;
+		
+	float ddx = dx - reflectionCutOff;
+	float ddy = dy - reflectionCutOff;
+	if (ddx > 0. && ddy > 0.) alpha = pow(alpha, ddx*ddy*reflectionIntence);
+		
+	vec4 col = texture2D(tex,c1)*(alpha);
+	gl_FragColor = col;
+}
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/underwater.lua b/game/modules/tome/data/gfx/shaders/main_fbo/underwater.lua
new file mode 100644
index 0000000000..75f8754ef3
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/underwater.lua
@@ -0,0 +1,28 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+return {
+	frag = "main_fbo/underwater",
+	vert = nil,
+	args = {
+		tex = { texture = 0 },
+	},
+	clone = false,
+	permanent = true,
+}
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/wobbling.frag b/game/modules/tome/data/gfx/shaders/main_fbo/wobbling.frag
new file mode 100644
index 0000000000..b00ca0cb3c
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/wobbling.frag
@@ -0,0 +1,165 @@
+uniform float wobbling;
+uniform float tick;
+uniform sampler2D noisevol;
+uniform vec2 texSize;
+uniform sampler2D tex;
+uniform vec2 mapCoord;
+
+const float PI = 3.1415926535897932;
+
+// play with these parameters to custimize the effect
+// ===================================================
+
+//speed
+const float speed = 0.05;
+const float speed_x = 0.3;
+const float speed_y = 0.3;
+
+// refraction
+const float emboss = 0.05;
+const float intensity = 0.4;
+const int steps = 6;
+const float frequency = 100.0;
+const int angle = 7; // better when a prime
+
+// reflection
+const float delta = 60.;
+const float intence = 700.;
+
+const float reflectionCutOff = 0.012;
+const float reflectionIntence = 200000.;
+
+float time = tick / 30000.0;
+
+float col(vec2 coord)
+{
+	float delta_theta = 2.0 * PI / float(angle);
+	float col = 0.0;
+	float theta = 0.0;
+	for (int i = 0; i < steps; i++)
+	{
+		vec2 adjc = coord;
+		theta = delta_theta*float(i);
+		adjc.x += cos(theta)*time*speed + time * speed_x;
+		adjc.y -= sin(theta)*time*speed - time * speed_y;
+		col = col + cos( (adjc.x*cos(theta) - adjc.y*sin(theta))*frequency)*intensity;
+	}
+
+	return cos(col);
+}
+
+vec4 permute( vec4 x ) {
+
+	return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 );
+
+} 
+
+vec4 taylorInvSqrt( vec4 r ) {
+
+	return 1.79284291400159 - 0.85373472095314 * r;
+
+}
+
+float snoise( vec3 v ) {
+
+	const vec2 C = vec2( 1.0 / 6.0, 1.0 / 3.0 );
+	const vec4 D = vec4( 0.0, 0.5, 1.0, 2.0 );
+
+	// First corner
+
+	vec3 i  = floor( v + dot( v, C.yyy ) );
+	vec3 x0 = v - i + dot( i, C.xxx );
+
+	// Other corners
+
+	vec3 g = step( x0.yzx, x0.xyz );
+	vec3 l = 1.0 - g;
+	vec3 i1 = min( g.xyz, l.zxy );
+	vec3 i2 = max( g.xyz, l.zxy );
+
+	vec3 x1 = x0 - i1 + 1.0 * C.xxx;
+	vec3 x2 = x0 - i2 + 2.0 * C.xxx;
+	vec3 x3 = x0 - 1. + 3.0 * C.xxx;
+
+	// Permutations
+
+	i = mod( i, 289.0 );
+	vec4 p = permute( permute( permute(
+		i.z + vec4( 0.0, i1.z, i2.z, 1.0 ) )
+		+ i.y + vec4( 0.0, i1.y, i2.y, 1.0 ) )
+		+ i.x + vec4( 0.0, i1.x, i2.x, 1.0 ) );
+
+	// Gradients
+	// ( N*N points uniformly over a square, mapped onto an octahedron.)
+
+	float n_ = 1.0 / 7.0; // N=7
+
+	vec3 ns = n_ * D.wyz - D.xzx;
+
+	vec4 j = p - 49.0 * floor( p * ns.z *ns.z );  //  mod(p,N*N)
+
+	vec4 x_ = floor( j * ns.z );
+	vec4 y_ = floor( j - 7.0 * x_ );    // mod(j,N)
+
+	vec4 x = x_ *ns.x + ns.yyyy;
+	vec4 y = y_ *ns.x + ns.yyyy;
+	vec4 h = 1.0 - abs( x ) - abs( y );
+
+	vec4 b0 = vec4( x.xy, y.xy );
+	vec4 b1 = vec4( x.zw, y.zw );
+
+
+	vec4 s0 = floor( b0 ) * 2.0 + 1.0;
+	vec4 s1 = floor( b1 ) * 2.0 + 1.0;
+	vec4 sh = -step( h, vec4( 0.0 ) );
+
+	vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
+	vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
+
+	vec3 p0 = vec3( a0.xy, h.x );
+	vec3 p1 = vec3( a0.zw, h.y );
+	vec3 p2 = vec3( a1.xy, h.z );
+	vec3 p3 = vec3( a1.zw, h.w );
+
+	// Normalise gradients
+
+	vec4 norm = taylorInvSqrt( vec4( dot( p0, p0 ), dot( p1, p1 ), dot( p2, p2 ), dot( p3, p3 ) ) );
+	p0 *= norm.x;
+	p1 *= norm.y;
+	p2 *= norm.z;
+	p3 *= norm.w;
+
+	// Mix final noise value
+
+	vec4 m = max( 0.6 - vec4( dot( x0, x0 ), dot( x1, x1 ), dot( x2, x2 ), dot( x3, x3 ) ), 0.0 );
+	m = m * m;
+	return 42.0 * dot( m*m, vec4( dot( p0, x0 ), dot( p1, x1 ),
+		dot( p2, x2 ), dot( p3, x3 ) ) );
+
+}  
+
+
+vec2 snoise2(vec3 pos)
+{
+	return vec2(snoise(pos), snoise(pos + vec3(0.0, 0.0, 1.0)));
+}
+
+void main(void)
+{
+	float scaledTime = tick / 5000.0;
+	vec2 coord = gl_TexCoord[0].xy;
+	coord.x *= texSize.x / texSize.y;
+	vec2 offset =
+		snoise2(vec3(coord / 2.0,  scaledTime / 0.25)) * 0.33 * 3.0 + 
+		snoise2(vec3(coord / 2.0, scaledTime / 2.0)) * 0.0 + 
+		snoise2(vec3(coord / 4.0, scaledTime / 4.0)) * 0.0;
+
+	offset.x *= texSize.x / texSize.y;
+
+	float ratio = clamp(1.5 * pow(length(vec2(0.5, 0.5) - gl_TexCoord[0].xy) / (0.7071), 2.0), 0.0, 1.0); //sqrt(2) / 2 = 0.7071
+	ratio *= (1.0 + snoise2(vec3(coord / 2.0, scaledTime / 0.25 + 10.0))) * 0.5;
+
+	gl_FragColor = 
+		texture2D(tex, gl_TexCoord[0].xy) * (1.0 - ratio) + 
+		texture2D(tex, gl_TexCoord[0].xy + offset * 0.01 * wobbling) * ratio;
+}
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/wobbling.lua b/game/modules/tome/data/gfx/shaders/main_fbo/wobbling.lua
new file mode 100644
index 0000000000..3f16a9ff41
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/wobbling.lua
@@ -0,0 +1,29 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+return {
+	frag = "main_fbo/wobbling",
+	vert = nil,
+	args = {
+		tex = { texture = 0 },
+		noisevol = { texture = 1 },
+	},
+	clone = false,
+	permanent = true,
+}
diff --git a/src/core_lua.c b/src/core_lua.c
index 57cf4951d9..b5047a9e43 100644
--- a/src/core_lua.c
+++ b/src/core_lua.c
@@ -2665,6 +2665,95 @@ static int gl_fbo_toscreen(lua_State *L)
 	return 0;
 }
 
+static int gl_fbo_posteffects(lua_State *L)
+{
+	lua_fbo *fbo = (lua_fbo*)auxiliar_checkclass(L, "gl{fbo}", 1);
+	lua_fbo *fbo2 = (lua_fbo*)auxiliar_checkclass(L, "gl{fbo}", 2);
+	lua_fbo *fbo_final = (lua_fbo*)auxiliar_checkclass(L, "gl{fbo}", 3);
+	lua_fbo *tmpfbo;
+	lua_fbo *srcfbo = fbo;
+	lua_fbo *dstfbo = fbo2;
+	int x = luaL_checknumber(L, 4);
+	int y = luaL_checknumber(L, 5);
+	int w = luaL_checknumber(L, 6);
+	int h = luaL_checknumber(L, 7);
+
+	glDisable(GL_BLEND);
+
+	GLfloat colors[4*4] = {
+		1, 1, 1, 1,
+		1, 1, 1, 1,
+		1, 1, 1, 1,
+		1, 1, 1, 1,
+	};
+	glColorPointer(4, GL_FLOAT, 0, colors);
+
+	GLfloat texcoords[2*4] = {
+		0, 1,
+		0, 0,
+		1, 0,
+		1, 1,
+	};
+	glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
+
+	GLfloat vertices[2*4] = {
+		0, 0,
+		0, h,
+		w, h,
+		w, 0,
+	};
+	glVertexPointer(2, GL_FLOAT, 0, vertices);
+
+	// Set the viewport and save the old one
+	glPushAttrib(GL_VIEWPORT_BIT);
+	glViewport(0, 0, fbo->w, fbo->h);
+	glMatrixMode(GL_PROJECTION);
+	glPushMatrix();
+	glLoadIdentity();
+	glOrtho(0, fbo->w, fbo->h, 0, -1001, 1001);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+
+	tglClearColor(0, 0, 0, 1);
+
+	int shad_idx = 8;
+	while (lua_isuserdata(L, shad_idx) && lua_isuserdata(L, shad_idx+1)) {
+		shader_type *s = (shader_type*)lua_touserdata(L, shad_idx);
+		useShader(s, fbo->w, fbo->h, w, h, 1, 1, 1, 1);
+
+		tglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstfbo->fbo);
+		glClear(GL_COLOR_BUFFER_BIT);
+		tglBindTexture(GL_TEXTURE_2D, srcfbo->texture);
+		glDrawArrays(GL_QUADS, 0, 4);
+
+		shad_idx++;
+		tmpfbo = srcfbo;
+		srcfbo = dstfbo;
+		dstfbo = tmpfbo;
+	}
+
+	// Bind final fbo (must have bee previously activated)
+	shader_type *s = (shader_type*)lua_touserdata(L, shad_idx);
+	useShader(s, fbo_final->w, fbo_final->h, w, h, 1, 1, 1, 1);
+	glMatrixMode(GL_PROJECTION);
+	glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glPopAttrib();
+	tglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_final->fbo);
+	glClear(GL_COLOR_BUFFER_BIT);
+	tglBindTexture(GL_TEXTURE_2D, srcfbo->texture);
+	vertices[0] = x; vertices[1] = y;
+	vertices[2] = x; vertices[3] = y + h;
+	vertices[4] = x + w; vertices[5] = y + h;
+	vertices[6] = x + w; vertices[7] = y;
+	glDrawArrays(GL_QUADS, 0, 4);
+
+	tglUseProgramObject(0);
+
+	glEnable(GL_BLEND);
+	return 0;
+}
+
 static int gl_fbo_is_active(lua_State *L)
 {
 	lua_pushboolean(L, fbo_active);
@@ -3051,6 +3140,7 @@ static const struct luaL_Reg gl_fbo_reg[] =
 {
 	{"__gc", gl_free_fbo},
 	{"toScreen", gl_fbo_toscreen},
+	{"postEffects", gl_fbo_posteffects},
 	{"use", gl_fbo_use},
 	{"png", gl_fbo_to_png},
 	{NULL, NULL},
-- 
GitLab