#version 450

struct Particle {
	vec4 position;
	vec4 velocity;
	vec4 gradient_pos;
};

layout(std140, binding = 0) buffer ParticleBuffer {
	Particle particles[];
};

layout (local_size_x = 256) in;



vec2 attraction(vec2 pos, vec2 attractPos)
{
    vec2 delta = attractPos - pos;
	const float damp = 0.5;
    float dDampedDot = dot(delta, delta) + damp;
    float invDist = 1.0f / sqrt(dDampedDot);
    float invDistCubed = invDist*invDist*invDist;
    return delta * invDistCubed * 0.0035;
}

vec2 repulsion(vec2 pos, vec2 attractPos)
{
	vec2 delta = attractPos - pos;
	float targetDistance = sqrt(dot(delta, delta));
	return delta * (1.0 / (targetDistance * targetDistance * targetDistance)) * -0.000035;
}

void main()
{
    uint particle_count = 1000;
    float dest_x = 0.0f;
    float dest_y = 0.0f;
    float delta_time = 0.001;
    // Current SSBO index
    uint index = gl_GlobalInvocationID.x;
	// Don't try to write beyond particle count
    if (index >= particle_count)
		return;

    // Read position and velocity
    vec2 vVel = particles[index].velocity.xy;
    vec2 vPos = particles[index].position.xy;

    vec2 destPos = vec2(dest_x, dest_y);

    vec2 delta = destPos - vPos;
    float targetDistance = sqrt(dot(delta, delta));
    vVel += repulsion(vPos, destPos.xy) * 0.05;

    // Move by velocity
    vPos += vVel * delta_time;

    // collide with boundary
    if ((vPos.x < -1.0) || (vPos.x > 1.0) || (vPos.y < -1.0) || (vPos.y > 1.0))
    	vVel = (-vVel * 0.1) + attraction(vPos, destPos) * 12;
    else
    	particles[index].position.xy = vPos;

    // Write back
    particles[index].velocity.xy = vVel;
	particles[index].gradient_pos.x += 0.02 * delta_time;
	if (particles[index].gradient_pos.x > 1.0)
		particles[index].gradient_pos.x -= 1.0;
	particles[index].position = vec4(0.0, index, index, 1.0f);

}
