#pragma kernel InitDust
#pragma kernel UpdateDust

struct Particle {
	float3 position;
	float3 velocity;
  uint enabled;
	uint airborne;
};

RWStructuredBuffer<float4> positions;
RWStructuredBuffer<Particle> particles;
RWStructuredBuffer<uint> numParticlesConsumed;
RWStructuredBuffer<uint> freeParticles;
uint numParticles;
uint particlesToInitialize;
float3 particleInitPos;
float3 initialVelocity;
float deltaTime;

float3 mop1Pos;
float3 mop2Pos;

float CleanRadius;

float size;
float gravity;
float particleVel;

// Hash function www.cs.ubc.ca/~rbridson/docs/schechter-sca08-turbulence.pdf
uint hash(uint state)
{
    state ^= 2747636419u;
    state *= 2654435769u;
    state ^= state >> 16;
    state *= 2654435769u;
    state ^= state >> 16;
    state *= 2654435769u;
    return state;
}

float scaleToRange01(uint state)
{
    return state / 4294967295.0;
}


[numthreads(64,1,1)]
void InitDust (uint3 id : SV_DispatchThreadID)
{
	if (id.x > particlesToInitialize) {
		return;
	}

	uint i = freeParticles[id.x];
	
	// if (particles[id.x].enabled != 0 || numParticlesInitialized[0] >= numParticles ) {
	while (i <= numParticles) {
		if (i == numParticles) {
			return;
		}

		if (particles[i].enabled == 0) {
			break;
		}

		i++;
	}

	uint randState = i;

	randState = hash(randState);
	float dv = scaleToRange01(randState) * 3.14f*2.0f;
	float dx = cos(dv);
	float dy = sin(dv);
	randState = hash(randState);
	float dz = scaleToRange01(randState);

	float3 nvel = float3(dx, dy, dz) * particleVel;

	randState = hash(randState);
	nvel.xy *= scaleToRange01(randState);

	randState = hash(randState);
	float3 iv = initialVelocity * scaleToRange01(randState);
	
	particles[i].velocity = iv + nvel;

	particles[i].position = particleInitPos;
	particles[i].position.z = 0.01;
	particles[i].enabled = 1;
	particles[i].airborne = 1;
	positions[i] = float4(particles[i].position, 1 * size);
	// particles[i].position = positions[i].xyz;

	// InterlockedAdd(numParticlesInitialized[0],1);
}

[numthreads(64,1,1)]
void UpdateDust (uint3 id : SV_DispatchThreadID)
{
	uint i = id.x;

  if (particles[i].enabled == 0) {
		return;
	}
	
	float3 pos = particles[i].position;

	if (particles[i].position.z > 0) {
		particles[i].velocity.z -= gravity * deltaTime;
	} else {
		particles[i].velocity -= particles[i].velocity * deltaTime * 15;
		
		if (particles[i].airborne == 1) {
			particles[i].airborne = 0;

			// Increase splattered particles
			InterlockedAdd(numParticlesConsumed[1],1);
		}

		float3 offset1 = mop1Pos - pos;
		offset1.z = 0;
		float dist1 = dot(offset1, offset1);

		float3 offset2 = mop2Pos - pos;
		offset2.z = 0;
		float dist2 = dot(offset2, offset2);

		float dist = min(dist1, dist2);
	
		if (dist < CleanRadius) {
			particles[i].enabled = 0;
			// Increase cleaned particles
			InterlockedAdd(numParticlesConsumed[0],1);
		}
	}

	particles[i].position += particles[i].velocity * deltaTime;
	positions[i] = float4(particles[i].position, particles[i].enabled * size);
}