-- $Id: SphereDistortion.lua 3171 2008-11-06 09:06:29Z det $
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

local SphereDistortion = {}
SphereDistortion.__index = SphereDistortion

local warpShader
local screenLoc, radiusLoc, strengthLoc, centerLoc

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function SphereDistortion.GetInfo()
	return {
		name      = "SphereDistortion",
		backup    = "", --// backup class, if this class doesn't work (old cards,ati's,etc.)
		desc      = "",

		layer     = 1, --// extreme simply z-ordering :x

		--// gfx requirement
		fbo       = true,
		shader    = true,
		distortion= true,
		intel     = 0,
	}
end

SphereDistortion.Default = {
	layer = 1,
	worldspace = true,

	life     = 25,
	pos      = {0,0,0},
	growth   = 2.5,
	strength = 0.15,

	repeatEffect = false,
	dieGameFrame = math.huge
}

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

local glUniform   = gl.Uniform
local glUseShader = gl.UseShader
local spWorldToScreenCoords = Spring.WorldToScreenCoords

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function SphereDistortion:BeginDrawDistortion()
	glUseShader(warpShader)
	glUniform(screenLoc, 1/vsx,1/vsy )
end

function SphereDistortion:EndDrawDistortion()
	glUseShader(0)
end


function SphereDistortion:DrawDistortion()
	local pos   = self.pos
	local x,y,z = pos[1],pos[2],pos[3]
	local cx,cy = spWorldToScreenCoords(x,y,z)
	glUniform(radiusLoc,   self.radius )
	glUniform(strengthLoc, self.strength )
	glUniform(centerLoc,   cx,cy )

	gl.BeginEnd(GL.QUADS,function()
		 gl.TexCoord(-1,1)
		gl.Vertex(x,y,z)
		 gl.TexCoord(1,1)
		gl.Vertex(x,y,z)
		 gl.TexCoord(1,-1)
		gl.Vertex(x,y,z)
		 gl.TexCoord(-1,-1)
		gl.Vertex(x,y,z)
	end)
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function SphereDistortion.Initialize()
	warpShader = gl.CreateShader({
		vertex = [[
			uniform vec2  center;
			uniform float radius;
			uniform vec2  screenInverse;

			varying vec2 texCoord;

			void main()
			{
				 gl_Position     = gl_ModelViewMatrix * gl_Vertex;
				 gl_Position.xy += gl_MultiTexCoord0.xy * radius;
				 gl_Position     = gl_ProjectionMatrix * gl_Position;
				 texCoord        = gl_MultiTexCoord0.st;
			}
		]],
		fragment = [[
			uniform vec2  center;
			uniform float strength;
			uniform vec2  screenInverse;

			varying vec2 texCoord;

			float p1 = gl_ProjectionMatrix[2][2];
			float p2 = gl_ProjectionMatrix[2][3];

			float ConvertZtoEye(float z)
			{
					return p2/(z*2.0-1.0+p1);
			}

			float ConvertEyeToZ(float d)
			{
					return 0.5-0.5*p1+(1.0/(2.0*d))*p2;
			}

			void main(void)
			{
					float dist = length(texCoord);
					if (dist>1.0) {
						discard;
					}else{
						float eyeDepth = ConvertZtoEye(gl_FragCoord.z);
						eyeDepth -= cos(asin(dist))*30.0;
						gl_FragDepth = ConvertEyeToZ(eyeDepth);

						vec2 d = gl_FragCoord.xy - center;
						float distortion = smoothstep(1.0,0.0,dist)*strength;
						vec2 noiseVec    = (d/dist)*screenInverse*distortion;
						gl_FragColor.xyw = vec3(noiseVec,gl_FragCoord.z);

						//float distortion = pow(dist, 1.0/4.0)-dist;
						//float distortion = exp( -0.5*( pow(-dist*6.0+2.5,2.0) ) )*0.25;
						//float distortion = tanh(dist*3.0)-dist;
						//float distortion = smoothstep(0.0,1.0,dist)-dist;
					}
			}
		]],
		uniform = {
			screenInverse = {1/1280,1/1024},
			strength = 0.15,
		}
	})

	if (warpShader == nil) then
		print(PRIO_MAJOR,"LUPS->SphereDistortion: critical shader error: "..gl.GetShaderLog())
		return false
	end

	screenLoc   = gl.GetUniformLocation(warpShader, 'screenInverse')
	strengthLoc = gl.GetUniformLocation(warpShader, 'strength')
	radiusLoc   = gl.GetUniformLocation(warpShader, 'radius')
	centerLoc   = gl.GetUniformLocation(warpShader, 'center')
end

function SphereDistortion.Finalize()
	if (gl.DeleteShader) then
		gl.DeleteShader(warpShader)
	end
end

function SphereDistortion.ViewResize()
	gl.UseShader(warpShader)
		gl.Uniform(screenLoc,1/vsx,1/vsy)
	gl.UseShader(0)
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function SphereDistortion:Update()
	self.radius = self.radius + self.growth
end

-- used if repeatEffect=true;
function SphereDistortion:ReInitialize()
	self.radius = 0

	self.dieGameFrame = self.dieGameFrame + self.life
end

function SphereDistortion:CreateParticle()
	self.radius = 0

	self.startGameFrame = thisGameFrame
	self.dieGameFrame   = self.startGameFrame + self.life
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

local MergeTable   = MergeTable
local setmetatable = setmetatable

function SphereDistortion.Create(Options)
	local newObject = MergeTable(Options, SphereDistortion.Default)
	setmetatable(newObject,SphereDistortion)  -- make handle lookup
	newObject:CreateParticle()
	return newObject
end

function SphereDistortion:Destroy()
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

return SphereDistortion
