MSTRINGIFY(


float adot3(float4 a, float4 b)
{
   return a.x*b.x + a.y*b.y + a.z*b.z;
}

float alength3(float4 a)
{
	a.w = 0;
	return length(a);
}

float4 anormalize3(float4 a)
{
	a.w = 0;
	return normalize(a);
}

float4 projectOnAxis( float4 v, float4 a )
{
	return (a*adot3(v, a));
}

__kernel void 
ApplyForcesKernel(
	const uint numNodes,
	const float solverdt,
	const float epsilon,
	__global int * g_vertexClothIdentifier,
	__global float4 * g_vertexNormal,
	__global float * g_vertexArea,
	__global float * g_vertexInverseMass,
	__global float * g_clothLiftFactor,
	__global float * g_clothDragFactor,
	__global float4 * g_clothWindVelocity,
	__global float4 * g_clothAcceleration,
	__global float * g_clothMediumDensity,
	__global float4 * g_vertexForceAccumulator,
	__global float4 * g_vertexVelocity GUID_ARG)
{
	unsigned int nodeID = get_global_id(0);
	if( nodeID < numNodes )
	{		
		int clothId  = g_vertexClothIdentifier[nodeID];
		float nodeIM = g_vertexInverseMass[nodeID];
		
		if( nodeIM > 0.0f )
		{
			float4 nodeV  = g_vertexVelocity[nodeID];
			float4 normal = g_vertexNormal[nodeID];
			float area    = g_vertexArea[nodeID];
			float4 nodeF  = g_vertexForceAccumulator[nodeID];
			
			// Read per-cloth values
			float4 clothAcceleration = g_clothAcceleration[clothId];
			float4 clothWindVelocity = g_clothWindVelocity[clothId];
			float liftFactor = g_clothLiftFactor[clothId];
			float dragFactor = g_clothDragFactor[clothId];
			float mediumDensity = g_clothMediumDensity[clothId];
		
			// Apply the acceleration to the cloth rather than do this via a force
			nodeV += (clothAcceleration*solverdt);

			g_vertexVelocity[nodeID] = nodeV;

			// Aerodynamics
			float4 rel_v = nodeV - clothWindVelocity;
			float rel_v_len = alength3(rel_v);
			float rel_v2 = dot(rel_v, rel_v);
			
			if( rel_v2 > epsilon )
			{
				float4 rel_v_nrm = anormalize3(rel_v);
				float4 nrm = normal;
									
				nrm = nrm * (dot(nrm, rel_v) < 0 ? -1.f : 1.f);

				float4 fDrag = (float4)(0.f, 0.f, 0.f, 0.f);
				float4 fLift = (float4)(0.f, 0.f, 0.f, 0.f);

				float n_dot_v = dot(nrm, rel_v_nrm);

				// drag force
				if ( dragFactor > 0.f )
					fDrag = 0.5f * dragFactor * mediumDensity * rel_v2 * area * n_dot_v * (-1.0f) * rel_v_nrm;

				// lift force
				// Check angle of attack
				// cos(10º) = 0.98480
				if ( 0 < n_dot_v && n_dot_v < 0.98480f)
					fLift = 0.5f * liftFactor * mediumDensity * rel_v_len * area * sqrt(1.0f-n_dot_v*n_dot_v) * (cross(cross(nrm, rel_v_nrm), rel_v_nrm));
				
				nodeF += fDrag + fLift;
					g_vertexForceAccumulator[nodeID] = nodeF;	
			}
		}
	}
}

);