MSTRINGIFY( //#pragma OPENCL EXTENSION cl_amd_printf:enable\n float mydot3a(float4 a, float4 b) { return a.x*b.x + a.y*b.y + a.z*b.z; } float mylength3(float4 a) { a.w = 0; return length(a); } float4 mynormalize3(float4 a) { a.w = 0; return normalize(a); } typedef struct { int firstObject; int endObject; } CollisionObjectIndices; typedef struct { float4 shapeTransform[4]; // column major 4x4 matrix float4 linearVelocity; float4 angularVelocity; int softBodyIdentifier; int collisionShapeType; // Shape information // Compressed from the union float radius; float halfHeight; int upAxis; float margin; float friction; int padding0; } CollisionShapeDescription; // From btBroadphaseProxy.h __constant int CAPSULE_SHAPE_PROXYTYPE = 10; // Multiply column-major matrix against vector float4 matrixVectorMul( float4 matrix[4], float4 vector ) { float4 returnVector; float4 row0 = (float4)(matrix[0].x, matrix[1].x, matrix[2].x, matrix[3].x); float4 row1 = (float4)(matrix[0].y, matrix[1].y, matrix[2].y, matrix[3].y); float4 row2 = (float4)(matrix[0].z, matrix[1].z, matrix[2].z, matrix[3].z); float4 row3 = (float4)(matrix[0].w, matrix[1].w, matrix[2].w, matrix[3].w); returnVector.x = dot(row0, vector); returnVector.y = dot(row1, vector); returnVector.z = dot(row2, vector); returnVector.w = dot(row3, vector); return returnVector; } __kernel void SolveCollisionsAndUpdateVelocitiesKernel( const int numNodes, const float isolverdt, __global int *g_vertexClothIdentifier, __global float4 *g_vertexPreviousPositions, __global float * g_perClothFriction, __global float * g_clothDampingFactor, __global CollisionObjectIndices * g_perClothCollisionObjectIndices, __global CollisionShapeDescription * g_collisionObjectDetails, __global float4 * g_vertexForces, __global float4 *g_vertexVelocities, __global float4 *g_vertexPositions, __local CollisionShapeDescription *localCollisionShapes, __global float * g_vertexInverseMasses) { int nodeID = get_global_id(0); float4 forceOnVertex = (float4)(0.f, 0.f, 0.f, 0.f); int clothIdentifier = g_vertexClothIdentifier[nodeID]; // Abort if this is not a valid cloth if( clothIdentifier < 0 ) return; float4 position = (float4)(g_vertexPositions[nodeID].xyz, 0.f); float4 previousPosition = (float4)(g_vertexPreviousPositions[nodeID].xyz, 0.f); float clothFriction = g_perClothFriction[clothIdentifier]; float dampingFactor = g_clothDampingFactor[clothIdentifier]; float velocityCoefficient = (1.f - dampingFactor); float4 difference = position - previousPosition; float4 velocity = difference*velocityCoefficient*isolverdt; float inverseMass = g_vertexInverseMasses[nodeID]; CollisionObjectIndices collisionObjectIndices = g_perClothCollisionObjectIndices[clothIdentifier]; int numObjects = collisionObjectIndices.endObject - collisionObjectIndices.firstObject; if( numObjects > 0 ) { // We have some possible collisions to deal with // First load all of the collision objects into LDS int numObjects = collisionObjectIndices.endObject - collisionObjectIndices.firstObject; if( get_local_id(0) < numObjects ) { localCollisionShapes[get_local_id(0)] = g_collisionObjectDetails[ collisionObjectIndices.firstObject + get_local_id(0) ]; } } // Safe as the vertices are padded so that not more than one soft body is in a group barrier(CLK_LOCAL_MEM_FENCE); // Annoyingly, even though I know the flow control is not varying, the compiler will not let me skip this if( numObjects > 0 ) { // We have some possible collisions to deal with for( int collision = 0; collision < numObjects; ++collision ) { CollisionShapeDescription shapeDescription = localCollisionShapes[collision]; float colliderFriction = localCollisionShapes[collision].friction; if( localCollisionShapes[collision].collisionShapeType == CAPSULE_SHAPE_PROXYTYPE ) { // Colliding with a capsule float capsuleHalfHeight = localCollisionShapes[collision].halfHeight; float capsuleRadius = localCollisionShapes[collision].radius; float capsuleMargin = localCollisionShapes[collision].margin; int capsuleupAxis = localCollisionShapes[collision].upAxis; if ( capsuleHalfHeight <= 0 ) capsuleHalfHeight = 0.0001f; float4 worldTransform[4]; worldTransform[0] = localCollisionShapes[collision].shapeTransform[0]; worldTransform[1] = localCollisionShapes[collision].shapeTransform[1]; worldTransform[2] = localCollisionShapes[collision].shapeTransform[2]; worldTransform[3] = localCollisionShapes[collision].shapeTransform[3]; // Correctly define capsule centerline vector float4 c1 = (float4)(0.f, 0.f, 0.f, 1.f); float4 c2 = (float4)(0.f, 0.f, 0.f, 1.f); c1.x = select( 0.f, -capsuleHalfHeight, capsuleupAxis == 0 ); c1.y = select( 0.f, -capsuleHalfHeight, capsuleupAxis == 1 ); c1.z = select( 0.f, -capsuleHalfHeight, capsuleupAxis == 2 ); c2.x = -c1.x; c2.y = -c1.y; c2.z = -c1.z; float4 worldC1 = matrixVectorMul(worldTransform, c1); float4 worldC2 = matrixVectorMul(worldTransform, c2); float4 segment = (float4)((worldC2 - worldC1).xyz, 0.f); float4 segmentNormalized = mynormalize3(segment); float distanceAlongSegment =mydot3a( (position - worldC1), segmentNormalized ); float4 closestPointOnSegment = (worldC1 + (float4)(segmentNormalized * distanceAlongSegment)); float distanceFromLine = mylength3(position - closestPointOnSegment); float distanceFromC1 = mylength3(worldC1 - position); float distanceFromC2 = mylength3(worldC2 - position); // Final distance from collision, point to push from, direction to push in // for impulse force float dist; float4 normalVector; if( distanceAlongSegment < 0 ) { dist = distanceFromC1; normalVector = (float4)(normalize(position - worldC1).xyz, 0.f); } else if( distanceAlongSegment > length(segment) ) { dist = distanceFromC2; normalVector = (float4)(normalize(position - worldC2).xyz, 0.f); } else { dist = distanceFromLine; normalVector = (float4)(normalize(position - closestPointOnSegment).xyz, 0.f); } float minDistance = capsuleRadius + capsuleMargin; float4 closestPointOnSurface = (float4)((position + (minDistance - dist) * normalVector).xyz, 0.f); float4 colliderLinearVelocity = shapeDescription.linearVelocity; float4 colliderAngularVelocity = shapeDescription.angularVelocity; float4 velocityOfSurfacePoint = colliderLinearVelocity + cross(colliderAngularVelocity, closestPointOnSurface - (float4)(worldTransform[0].w, worldTransform[1].w, worldTransform[2].w, 0.f)); // Check for a collision if( dist < minDistance ) { // Project back to surface along normal position = closestPointOnSurface; velocity = (position - previousPosition) * velocityCoefficient * isolverdt; float4 relativeVelocity = velocity - velocityOfSurfacePoint; float4 p1 = mynormalize3(cross(normalVector, segment)); float4 p2 = mynormalize3(cross(p1, normalVector)); float4 tangentialVel = p1*mydot3a(relativeVelocity, p1) + p2*mydot3a(relativeVelocity, p2); float frictionCoef = (colliderFriction * clothFriction); if (frictionCoef>1.f) frictionCoef = 1.f; //only apply friction if objects are not moving apart float projVel = mydot3a(relativeVelocity,normalVector); if ( projVel >= -0.001f) { if ( inverseMass > 0 ) { //float4 myforceOnVertex = -tangentialVel * frictionCoef * isolverdt * (1.0f / inverseMass); position += (-tangentialVel * frictionCoef) / (isolverdt); } } // In case of no collision, this is the value of velocity velocity = (position - previousPosition) * velocityCoefficient * isolverdt; } } } } g_vertexVelocities[nodeID] = (float4)(velocity.xyz, 0.f); // Update external force g_vertexForces[nodeID] = (float4)(forceOnVertex.xyz, 0.f); g_vertexPositions[nodeID] = (float4)(position.xyz, 0.f); } );