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

local ShockWave = {}
ShockWave.__index = ShockWave

local warpShader
local screenLoc
local dlist

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

function ShockWave.GetInfo()
	return {
		name      = "ShockWave",
		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

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

	life   = 23,
	pos    = {0,0,0},
	growth = 4.5,

	repeatEffect = false,
	dieGameFrame = math.huge
}

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

local glUniform   = gl.Uniform
local glUseShader = gl.UseShader
local glCallList  = gl.CallList
local glMultiTexCoord = gl.MultiTexCoord
local spWorldToScreenCoords = Spring.WorldToScreenCoords

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

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

function ShockWave:EndDrawDistortion()
	glUseShader(0)
end


function ShockWave:DrawDistortion()
	local pos   = self.pos
	local x,y,z = pos[1],pos[2],pos[3]
	local cx,cy = spWorldToScreenCoords(x,y,z)

	glMultiTexCoord(0,x,y,z,1)
	glMultiTexCoord(1,cx,cy,self.radius,self.life2)

	glCallList(dlist)
end

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

function ShockWave.Initialize()
	warpShader = gl.CreateShader({
		vertex = [[
			uniform float radius;

			varying vec2  center;
			varying float life;
			varying vec2  texCoord;

			void main()
			{
				 center   = gl_MultiTexCoord1.xy;
				 life     = gl_MultiTexCoord1.w;
				 texCoord = gl_Vertex.st;

				 gl_Position     = gl_ModelViewMatrix * gl_MultiTexCoord0;
				 gl_Position.xy += gl_Vertex.xy * gl_MultiTexCoord1.z;
				 gl_Position     = gl_ProjectionMatrix * gl_Position;
			}
		]],
		fragment = [[
			uniform vec2  screenInverse;

			varying vec2  center;
			varying float life;
			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)-0.6)*2.5;
					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 = exp( -0.5*( pow(-dist*8.0+4.0,2.0) ) )*0.15;
						vec2 noiseVec    = (d/dist)*screenInverse*distortion*life;
						gl_FragColor.xyw = vec3(noiseVec,gl_FragCoord.z);

						//float distortion = pow(dist, 1.0/4.0)-dist;
						//float distortion = smoothstep(1.0,0.0,dist)*0.25;
						//float distortion = tanh(dist*3.0)-dist;
						//float distortion = smoothstep(0.0,1.0,dist)-dist;
					}
			}
		]],
		uniform = {
			screenInverse = {1/1280,1/1024},
			life = 1,
		}
	})

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

	screenLoc = gl.GetUniformLocation(warpShader, 'screenInverse')

	dlist = gl.CreateList(gl.BeginEnd,GL.QUADS,function()
		gl.Vertex(-1,1)
		gl.Vertex(1,1)
		gl.Vertex(1,-1)
		gl.Vertex(-1,-1)
	end)
end

function ShockWave.Finalize()
	if (gl.DeleteShader) then
		gl.DeleteShader(warpShader)
	end
	gl.DeleteList(dlist)
end

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

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

function ShockWave:Update()
	self.life2  = self.life2 - self.life_inc
	self.radius = self.radius + self.growth
end

-- used if repeatEffect=true;
function ShockWave:ReInitialize()
	self.life2  = 1
	self.radius = 0
	self.startGameFrame = thisGameFrame
	self.dieGameFrame = self.startGameFrame + self.life
end

function ShockWave:CreateParticle()
	self.startGameFrame = thisGameFrame
	self.dieGameFrame   = self.startGameFrame + self.life

	self.life2 = 1
	self.life_inc = 1/(self.dieGameFrame - self.startGameFrame)
	self.radius = 0
end

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

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

function ShockWave:Destroy()
end

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

return ShockWave
