//#version 140 // textureSize()&texelFetch() needs at least version 130, gl_InstanceID needs at least version 140
#extension GL_EXT_gpu_shader4 : enable

attribute vec3 a_position;
uniform vec4 u_blendcolor;
uniform mat4 u_mvpMtx;
uniform int u_id;

uniform sampler2D s_tex0; // the noise texture
uniform vec2 u_noiseTextureDim;
uniform sampler2D s_tex1; // the forcegrid texture
uniform vec2 u_forceGridTextureDim;

uniform float u_emissionRate;
uniform float u_duration; // the temporal interval in which PS emits new particles

uniform float u_particlesInSystem; // the instance count
uniform float u_spawnTime;
uniform float time;
uniform vec2 u_lifetimeRange;

uniform vec2 u_angularRangeRad;
uniform vec2 u_excentricityRange;
uniform vec2 u_angularVelocityRangeRad;
uniform vec2 u_scaleRange;
uniform vec2 u_targetScaleRange;

uniform vec4 u_spawnPosRange;
// uniform bool u_spawnInArea;
uniform bool u_centrifugal;

uniform vec4 u_velocityRange;
uniform vec2 u_force;
uniform float u_mass;

uniform float u_colorVariation;
uniform bool u_fadeOut;

uniform vec2 u_forceGridOffset;
uniform float u_forceGridCellsize;
uniform float u_forceGridT;


// #define MAX_PATH_POINTS	32
// uniform int u_numPathPoints;
// uniform bool u_closed;
// uniform vec2 u_pathPoints[MAX_PATH_POINTS];

varying vec4 v_instanceColor;

#define SKIP gl_Position = vec4(vec3(-10.0), 1.0); return;

float spawnTime = u_spawnTime;

vec2 pos2D;
float sys_dt = time-spawnTime; // time relative to PS birth

// variation for this particular particle within the PS
float fInstanceID;
vec2 tc;
vec4 noiseData;


vec3 getInitialPosition()
{
	vec2 initialPos2D = vec2(0.0,0.0);
	float particleAngle = 0.0;

	// POSITION	BY SPAWN TYPE	
	//initialPos2D.x += mix(u_spawnPosRange.x, u_spawnPosRange.y, noiseData.r);
	//initialPos2D.y += mix(u_spawnPosRange.z, u_spawnPosRange.w, noiseData.a);
	initialPos2D.xy += mix(u_spawnPosRange.xz, u_spawnPosRange.yw, noiseData.ra);

	if (u_centrifugal)
	{
		// initialVelocity = normalize(initialPos2D);
		particleAngle = atan(initialPos2D.x, initialPos2D.y);
	}

	return vec3(initialPos2D, particleAngle);
}

// float getParticleAlpha(in float particle_dt, in float particleLifeDuration)
// {
// 	// currently spreads alpha evenly in thirds of particleLifeDuration
// 	return	( (particle_dt <= 0.333*particleLifeDuration) ? 	
// 					((particle_dt-0.0)/(0.333*particleLifeDuration)) :
// 					( (particle_dt <= 0.666*particleLifeDuration) ?
// 						1.0	:
// 						(1.0-(particle_dt-0.666*particleLifeDuration)/(0.333*particleLifeDuration))		 )			
// 			);
	
// }

void createParticles()
{
	vec3 initialPos2D = getInitialPosition();
	vec2 forceFromGrid = texture2D(s_tex1, (initialPos2D.xy+u_forceGridOffset)/(u_forceGridCellsize*u_forceGridTextureDim) ).rg;

	float particleLifeDuration = mix(u_lifetimeRange.x, u_lifetimeRange.y, noiseData.r);

	//if( dot(forceFromGrid, forceFromGrid) > u_forceGridT ) // we'll ultimately want this to avoid the sqrt()
	if( length(forceFromGrid) > u_forceGridT ) // this however works more intuitively in the editor!
	{
		particleLifeDuration *= 3.0;
	}
	else
	{
		SKIP
	}
	

	// variation within [-1;+1]
	vec4 noiseData_m1p1 = (noiseData-0.5)*2.0;

	float particleEmissionRateBirthTime = fInstanceID / u_emissionRate;
	float particleEmissionRateDeathTime = particleEmissionRateBirthTime+particleLifeDuration;
	float particle_dt = sys_dt - particleEmissionRateBirthTime;
	
	// don't spawn a new particle since PS stops emitting
	// duration <= 0.0 means PS emits all the time
	if(u_duration > 0.0)
	{
		if( particleEmissionRateBirthTime >= u_duration )
		{
			SKIP
		}
	}
	else // emitter should never stop to emit particles
	{
		float exhaustDuration = u_particlesInSystem / u_emissionRate;
		float emitterExhaustLifeCycles = floor(sys_dt / (exhaustDuration));

		if( particleEmissionRateBirthTime + (emitterExhaustLifeCycles-1.0) * exhaustDuration + particleLifeDuration > sys_dt)
		{
			--emitterExhaustLifeCycles;
		}
		
		// this makes the particle system start out 'unexpanded'
		// when the emitter starts emitting particles in the very beginning
		if( emitterExhaustLifeCycles<0.0 )
		{
			SKIP
		}
		
		particleEmissionRateBirthTime += emitterExhaustLifeCycles * exhaustDuration;
		particleEmissionRateDeathTime += emitterExhaustLifeCycles * exhaustDuration;
		particle_dt = sys_dt - particleEmissionRateBirthTime;
	}

	// hold back particles based on emission rate
	if(particleEmissionRateBirthTime+particleLifeDuration < sys_dt)
	{
		SKIP // already dead
	}

	if(particleEmissionRateBirthTime > sys_dt)
	{
		SKIP // not yet time for birth
	}

	float particle_lifeRelative = particle_dt/particleLifeDuration;

	// simulation block:
	// 1. determine particles' seed values
	// 2. calculate other attributes based on the particles' 'age' => the actual particle simulation
	{
		// assume particle system never changes its spawn area during its lifetime
		// otherwise this calculation breaks!
		// this actually means that we don't need to distinguish between the various life cycle phase

		// vec2 initialPos2D;
		// initialPos2D.x = mix(u_spawnPosRange.x, u_spawnPosRange.y, noiseData.r);
		// initialPos2D.y = mix(u_spawnPosRange.z, u_spawnPosRange.w, noiseData.a);


		
		vec2 initialVelocity = vec2(0.0,0.0);
		float particleAngle = initialPos2D.z;

		// ANGLE
		particleAngle += mix(u_angularVelocityRangeRad.x, u_angularVelocityRangeRad.y, noiseData.r) * particle_dt;
		particleAngle += mix(u_angularRangeRad.x, u_angularRangeRad.y, noiseData.b);
		float cosVelRad = cos(particleAngle);
		float sinVelRad = sin(particleAngle);
		mat2 rotMat = mat2(	cosVelRad, -sinVelRad,   	//first column
	  						sinVelRad,  cosVelRad );  //second column

		// EXCENTRICITY AND SCALE
		float excentricity = mix(u_excentricityRange.x, u_excentricityRange.y, noiseData.g);
	  	float scaleFactor = mix(u_scaleRange.x, u_scaleRange.y, noiseData.b);
	  	float targetScaleFactor = mix(u_targetScaleRange.x, u_targetScaleRange.y, noiseData.b);
	  	scaleFactor = mix(scaleFactor, scaleFactor*targetScaleFactor, particle_lifeRelative);

	  	// FORCES
		//initialVelocity.x += mix(u_velocityRange.x, u_velocityRange.y, noiseData.g);
		//initialVelocity.y += mix(u_velocityRange.z, u_velocityRange.w, noiseData.b);
		initialVelocity.xy += mix(u_velocityRange.xz, u_velocityRange.yw, noiseData.gb);

		//initialVelocity += texture2D(s_tex1, (initialPos2D.xy+u_forceGridOffset)/(u_forceGridCellsize*u_forceGridTextureDim) ).rg;
		initialVelocity += forceFromGrid;


		// v_instanceColor = vec4( texture2D(s_tex1, (initialPos2D.xy+u_forceGridOffset)/(u_forceGridCellsize*u_forceGridTextureDim) ).rg, 1.0,1.0 );
	

		vec2 acceleration = u_force / u_mass;

		// SIMULATION

		// http://www.gamasutra.com/view/feature/130535/building_a_millionparticle_system.php?print=1
		pos2D = initialPos2D.xy + particle_dt*initialVelocity + 0.5*particle_dt*particle_dt*acceleration;
		// NOTE: rather than dt we should use the current wrapping relative lifetime in its lifecycle

		// pos2D is the particle center, so the rendered primitive
		// would collapse to a single point (which in the case of e.g. a triangle
		// would render nothing on screen!) ==>
		// add object space position offset of each vertex relative to 
		// the rendered primitive
		// pos2D += (rotMat*a_position.xy)*0.125;
		pos2D += (rotMat*(scaleFactor*(a_position.xy+vec2(excentricity, 0.0))));
		//pos2D += (rotMat*(scaleFactor*(rotMat*a_position.xy+vec2(sin(time)*excentricity, 0.0))));
	}
	
	v_instanceColor = vec4(1.0,1.0,1.0,1.0);
	if (u_colorVariation > 0.0)
		v_instanceColor.rgb *= (noiseData.r + noiseData.g + noiseData.b)/3.0 * u_colorVariation;
	// if (u_fadeOut)
	// 	v_instanceColor.a = clamp(min(3.0*particle_lifeRelative, 3.0-3.0*particle_lifeRelative), 0.0, 1.0);
	if (u_fadeOut)
		v_instanceColor.a *= (particleLifeDuration-particle_dt)/particleLifeDuration;
		
	gl_Position = u_mvpMtx * vec4(pos2D, a_position.z, 1.0);

	// just check if gl_InstanceID is working (should simply translate each triangle to the right a bit)
	//gl_Position = u_mvpMtx * vec4(a_position.xy+vec2(gl_InstanceID,0.0), a_position.z, 1.0);	
}


void main()
{
	// variation for this particular particle within the PS
	fInstanceID = float(gl_InstanceID);
	tc = ( vec2( mod(fInstanceID, u_noiseTextureDim.x), fInstanceID/(u_noiseTextureDim.x) ) + vec2(0.5) ) / (u_noiseTextureDim);
	noiseData = texture2D(s_tex0, tc);

	createParticles();
} 


