added missing GPU cloth simulation files and DX11/OpenCL kernels
Thanks to Cameron Hart for the report, see Issue 486
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
MSTRINGIFY(
|
||||
|
||||
cbuffer ComputeBoundsCB : register( b0 )
|
||||
{
|
||||
int numNodes;
|
||||
int numSoftBodies;
|
||||
int padding1;
|
||||
int padding2;
|
||||
};
|
||||
|
||||
// Node indices for each link
|
||||
StructuredBuffer<int> g_vertexClothIdentifier : register( t0 );
|
||||
StructuredBuffer<float4> g_vertexPositions : register( t1 );
|
||||
|
||||
RWStructuredBuffer<uint4> g_clothMinBounds : register( u0 );
|
||||
RWStructuredBuffer<uint4> g_clothMaxBounds : register( u1 );
|
||||
|
||||
groupshared uint4 clothMinBounds[256];
|
||||
groupshared uint4 clothMaxBounds[256];
|
||||
|
||||
[numthreads(128, 1, 1)]
|
||||
void
|
||||
ComputeBoundsKernel( uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex )
|
||||
{
|
||||
const unsigned int UINT_MAX = 0xffffffff;
|
||||
|
||||
// Init min and max bounds arrays
|
||||
if( GTid.x < numSoftBodies )
|
||||
{
|
||||
clothMinBounds[GTid.x] = uint4(UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX);
|
||||
clothMaxBounds[GTid.x] = uint4(0,0,0,0);
|
||||
}
|
||||
|
||||
AllMemoryBarrierWithGroupSync();
|
||||
|
||||
int nodeID = DTid.x;
|
||||
if( nodeID < numNodes )
|
||||
{
|
||||
int clothIdentifier = g_vertexClothIdentifier[nodeID];
|
||||
if( clothIdentifier >= 0 )
|
||||
{
|
||||
float3 position = g_vertexPositions[nodeID].xyz;
|
||||
|
||||
// Reinterpret position as uint
|
||||
uint3 positionUInt = uint3(asuint(position.x), asuint(position.y), asuint(position.z));
|
||||
|
||||
// Invert sign bit of positives and whole of negatives to allow comparison as unsigned ints
|
||||
//positionUInt.x ^= uint((-int(positionUInt.x >> 31) | 0x80000000));
|
||||
//positionUInt.y ^= uint((-int(positionUInt.y >> 31) | 0x80000000));
|
||||
//positionUInt.z ^= uint((-int(positionUInt.z >> 31) | 0x80000000));
|
||||
positionUInt.x ^= (1+~(positionUInt.x >> 31) | 0x80000000);
|
||||
positionUInt.y ^= (1+~(positionUInt.y >> 31) | 0x80000000);
|
||||
positionUInt.z ^= (1+~(positionUInt.z >> 31) | 0x80000000);
|
||||
|
||||
// Min/max with the LDS values
|
||||
InterlockedMin(clothMinBounds[clothIdentifier].x, positionUInt.x);
|
||||
InterlockedMin(clothMinBounds[clothIdentifier].y, positionUInt.y);
|
||||
InterlockedMin(clothMinBounds[clothIdentifier].z, positionUInt.z);
|
||||
|
||||
InterlockedMax(clothMaxBounds[clothIdentifier].x, positionUInt.x);
|
||||
InterlockedMax(clothMaxBounds[clothIdentifier].y, positionUInt.y);
|
||||
InterlockedMax(clothMaxBounds[clothIdentifier].z, positionUInt.z);
|
||||
}
|
||||
}
|
||||
|
||||
AllMemoryBarrierWithGroupSync();
|
||||
|
||||
|
||||
// Use global atomics to update the global versions of the data
|
||||
if( GTid.x < numSoftBodies )
|
||||
{
|
||||
InterlockedMin(g_clothMinBounds[GTid.x].x, clothMinBounds[GTid.x].x);
|
||||
InterlockedMin(g_clothMinBounds[GTid.x].y, clothMinBounds[GTid.x].y);
|
||||
InterlockedMin(g_clothMinBounds[GTid.x].z, clothMinBounds[GTid.x].z);
|
||||
|
||||
InterlockedMax(g_clothMaxBounds[GTid.x].x, clothMaxBounds[GTid.x].x);
|
||||
InterlockedMax(g_clothMaxBounds[GTid.x].y, clothMaxBounds[GTid.x].y);
|
||||
InterlockedMax(g_clothMaxBounds[GTid.x].z, clothMaxBounds[GTid.x].z);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
);
|
||||
@@ -0,0 +1,170 @@
|
||||
MSTRINGIFY(
|
||||
|
||||
cbuffer SolvePositionsFromLinksKernelCB : register( b0 )
|
||||
{
|
||||
unsigned int numNodes;
|
||||
float isolverdt;
|
||||
int padding0;
|
||||
int padding1;
|
||||
};
|
||||
|
||||
struct CollisionObjectIndices
|
||||
{
|
||||
int firstObject;
|
||||
int endObject;
|
||||
};
|
||||
|
||||
struct CollisionShapeDescription
|
||||
{
|
||||
float4x4 shapeTransform;
|
||||
float4 linearVelocity;
|
||||
float4 angularVelocity;
|
||||
|
||||
int softBodyIdentifier;
|
||||
int collisionShapeType;
|
||||
|
||||
|
||||
// Shape information
|
||||
// Compressed from the union
|
||||
float radius;
|
||||
float halfHeight;
|
||||
|
||||
float margin;
|
||||
float friction;
|
||||
|
||||
int padding0;
|
||||
int padding1;
|
||||
|
||||
};
|
||||
|
||||
// From btBroadphaseProxy.h
|
||||
static const int CAPSULE_SHAPE_PROXYTYPE = 10;
|
||||
|
||||
// Node indices for each link
|
||||
StructuredBuffer<int> g_vertexClothIdentifier : register( t0 );
|
||||
StructuredBuffer<float4> g_vertexPreviousPositions : register( t1 );
|
||||
StructuredBuffer<float> g_perClothFriction : register( t2 );
|
||||
StructuredBuffer<float> g_clothDampingFactor : register( t3 );
|
||||
StructuredBuffer<CollisionObjectIndices> g_perClothCollisionObjectIndices : register( t4 );
|
||||
StructuredBuffer<CollisionShapeDescription> g_collisionObjectDetails : register( t5 );
|
||||
|
||||
RWStructuredBuffer<float4> g_vertexForces : register( u0 );
|
||||
RWStructuredBuffer<float4> g_vertexVelocities : register( u1 );
|
||||
RWStructuredBuffer<float4> g_vertexPositions : register( u2 );
|
||||
|
||||
[numthreads(128, 1, 1)]
|
||||
void
|
||||
SolveCollisionsAndUpdateVelocitiesKernel( uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex )
|
||||
{
|
||||
int nodeID = DTid.x;
|
||||
float3 forceOnVertex = float3(0.f, 0.f, 0.f);
|
||||
if( DTid.x < numNodes )
|
||||
{
|
||||
int clothIdentifier = g_vertexClothIdentifier[nodeID];
|
||||
float4 position = float4(g_vertexPositions[nodeID].xyz, 1.f);
|
||||
float4 previousPosition = float4(g_vertexPreviousPositions[nodeID].xyz, 1.f);
|
||||
float3 velocity;
|
||||
float clothFriction = g_perClothFriction[clothIdentifier];
|
||||
float dampingFactor = g_clothDampingFactor[clothIdentifier];
|
||||
float velocityCoefficient = (1.f - dampingFactor);
|
||||
CollisionObjectIndices collisionObjectIndices = g_perClothCollisionObjectIndices[clothIdentifier];
|
||||
|
||||
if( collisionObjectIndices.firstObject != collisionObjectIndices.endObject )
|
||||
{
|
||||
velocity = float3(15, 0, 0);
|
||||
|
||||
// We have some possible collisions to deal with
|
||||
for( int collision = collisionObjectIndices.firstObject; collision < collisionObjectIndices.endObject; ++collision )
|
||||
{
|
||||
CollisionShapeDescription shapeDescription = g_collisionObjectDetails[collision];
|
||||
float colliderFriction = shapeDescription.friction;
|
||||
|
||||
if( shapeDescription.collisionShapeType == CAPSULE_SHAPE_PROXYTYPE )
|
||||
{
|
||||
// Colliding with a capsule
|
||||
|
||||
float capsuleHalfHeight = shapeDescription.halfHeight;
|
||||
float capsuleRadius = shapeDescription.radius;
|
||||
float capsuleMargin = shapeDescription.margin;
|
||||
float4x4 worldTransform = shapeDescription.shapeTransform;
|
||||
|
||||
float4 c1 = float4(0.f, -capsuleHalfHeight, 0.f, 1.f);
|
||||
float4 c2 = float4(0.f, +capsuleHalfHeight, 0.f, 1.f);
|
||||
float4 worldC1 = mul(worldTransform, c1);
|
||||
float4 worldC2 = mul(worldTransform, c2);
|
||||
float3 segment = (worldC2 - worldC1).xyz;
|
||||
|
||||
// compute distance of tangent to vertex along line segment in capsule
|
||||
float distanceAlongSegment = -( dot( (worldC1 - position).xyz, segment ) / dot(segment, segment) );
|
||||
|
||||
float4 closestPoint = (worldC1 + float4(segment * distanceAlongSegment, 0.f));
|
||||
float distanceFromLine = length(position - closestPoint);
|
||||
float distanceFromC1 = length(worldC1 - position);
|
||||
float distanceFromC2 = length(worldC2 - position);
|
||||
|
||||
// Final distance from collision, point to push from, direction to push in
|
||||
// for impulse force
|
||||
float dist;
|
||||
float3 normalVector;
|
||||
if( distanceAlongSegment < 0 )
|
||||
{
|
||||
dist = distanceFromC1;
|
||||
normalVector = normalize(position - worldC1).xyz;
|
||||
} else if( distanceAlongSegment > 1.f ) {
|
||||
dist = distanceFromC2;
|
||||
normalVector = normalize(position - worldC2).xyz;
|
||||
} else {
|
||||
dist = distanceFromLine;
|
||||
normalVector = normalize(position - closestPoint).xyz;
|
||||
}
|
||||
|
||||
float3 colliderLinearVelocity = shapeDescription.linearVelocity.xyz;
|
||||
float3 colliderAngularVelocity = shapeDescription.angularVelocity.xyz;
|
||||
float3 velocityOfSurfacePoint = colliderLinearVelocity + cross(colliderAngularVelocity, position.xyz - worldTransform._m03_m13_m23);
|
||||
|
||||
float minDistance = capsuleRadius + capsuleMargin;
|
||||
|
||||
// In case of no collision, this is the value of velocity
|
||||
velocity = (position - previousPosition).xyz * velocityCoefficient * isolverdt;
|
||||
|
||||
|
||||
// Check for a collision
|
||||
if( dist < minDistance )
|
||||
{
|
||||
// Project back to surface along normal
|
||||
position = position + float4((minDistance - dist)*normalVector*0.9, 0.f);
|
||||
velocity = (position - previousPosition).xyz * velocityCoefficient * isolverdt;
|
||||
float3 relativeVelocity = velocity - velocityOfSurfacePoint;
|
||||
|
||||
float3 p1 = normalize(cross(normalVector, segment));
|
||||
float3 p2 = normalize(cross(p1, normalVector));
|
||||
// Full friction is sum of velocities in each direction of plane
|
||||
float3 frictionVector = p1*dot(relativeVelocity, p1) + p2*dot(relativeVelocity, p2);
|
||||
|
||||
// Real friction is peak friction corrected by friction coefficients
|
||||
frictionVector = frictionVector * (colliderFriction*clothFriction);
|
||||
|
||||
float approachSpeed = dot(relativeVelocity, normalVector);
|
||||
|
||||
if( approachSpeed <= 0.0 )
|
||||
forceOnVertex -= frictionVector;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Update velocity
|
||||
float3 difference = position.xyz - previousPosition.xyz;
|
||||
velocity = difference*velocityCoefficient*isolverdt;
|
||||
}
|
||||
|
||||
g_vertexVelocities[nodeID] = float4(velocity, 0.f);
|
||||
|
||||
// Update external force
|
||||
g_vertexForces[nodeID] = float4(forceOnVertex, 0.f);
|
||||
|
||||
g_vertexPositions[nodeID] = float4(position.xyz, 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
@@ -0,0 +1,191 @@
|
||||
MSTRINGIFY(
|
||||
|
||||
cbuffer SolvePositionsFromLinksKernelCB : register( b0 )
|
||||
{
|
||||
unsigned int numNodes;
|
||||
float isolverdt;
|
||||
int padding0;
|
||||
int padding1;
|
||||
};
|
||||
|
||||
struct CollisionObjectIndices
|
||||
{
|
||||
int firstObject;
|
||||
int endObject;
|
||||
};
|
||||
|
||||
struct CollisionShapeDescription
|
||||
{
|
||||
float4x4 shapeTransform;
|
||||
float4 linearVelocity;
|
||||
float4 angularVelocity;
|
||||
|
||||
int softBodyIdentifier;
|
||||
int collisionShapeType;
|
||||
|
||||
|
||||
// Shape information
|
||||
// Compressed from the union
|
||||
float radius;
|
||||
float halfHeight;
|
||||
|
||||
float margin;
|
||||
float friction;
|
||||
|
||||
int padding0;
|
||||
int padding1;
|
||||
|
||||
};
|
||||
|
||||
// From btBroadphaseProxy.h
|
||||
static const int CAPSULE_SHAPE_PROXYTYPE = 10;
|
||||
|
||||
// Node indices for each link
|
||||
StructuredBuffer<int> g_vertexClothIdentifier : register( t0 );
|
||||
StructuredBuffer<float4> g_vertexPreviousPositions : register( t1 );
|
||||
StructuredBuffer<float> g_perClothFriction : register( t2 );
|
||||
StructuredBuffer<float> g_clothDampingFactor : register( t3 );
|
||||
StructuredBuffer<CollisionObjectIndices> g_perClothCollisionObjectIndices : register( t4 );
|
||||
StructuredBuffer<CollisionShapeDescription> g_collisionObjectDetails : register( t5 );
|
||||
|
||||
RWStructuredBuffer<float4> g_vertexForces : register( u0 );
|
||||
RWStructuredBuffer<float4> g_vertexVelocities : register( u1 );
|
||||
RWStructuredBuffer<float4> g_vertexPositions : register( u2 );
|
||||
|
||||
// A buffer of local collision shapes
|
||||
// TODO: Iterate to support more than 16
|
||||
groupshared CollisionShapeDescription localCollisionShapes[16];
|
||||
|
||||
[numthreads(128, 1, 1)]
|
||||
void
|
||||
SolveCollisionsAndUpdateVelocitiesKernel( uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex )
|
||||
{
|
||||
int nodeID = DTid.x;
|
||||
float3 forceOnVertex = float3(0.f, 0.f, 0.f);
|
||||
|
||||
int clothIdentifier = g_vertexClothIdentifier[nodeID];
|
||||
float4 position = float4(g_vertexPositions[nodeID].xyz, 1.f);
|
||||
float4 previousPosition = float4(g_vertexPreviousPositions[nodeID].xyz, 1.f);
|
||||
float3 velocity;
|
||||
float clothFriction = g_perClothFriction[clothIdentifier];
|
||||
float dampingFactor = g_clothDampingFactor[clothIdentifier];
|
||||
float velocityCoefficient = (1.f - dampingFactor);
|
||||
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( GTid.x < numObjects )
|
||||
{
|
||||
localCollisionShapes[GTid.x] = g_collisionObjectDetails[ collisionObjectIndices.firstObject + GTid.x ];
|
||||
}
|
||||
}
|
||||
|
||||
// Safe as the vertices are padded so that not more than one soft body is in a group
|
||||
AllMemoryBarrierWithGroupSync();
|
||||
|
||||
// Annoyingly, even though I know the flow control is not varying, the compiler will not let me skip this
|
||||
if( numObjects > 0 )
|
||||
{
|
||||
velocity = float3(0, 0, 0);
|
||||
|
||||
|
||||
// We have some possible collisions to deal with
|
||||
for( int collision = 0; collision < numObjects; ++collision )
|
||||
{
|
||||
CollisionShapeDescription shapeDescription = localCollisionShapes[collision];
|
||||
float colliderFriction = shapeDescription.friction;
|
||||
|
||||
if( shapeDescription.collisionShapeType == CAPSULE_SHAPE_PROXYTYPE )
|
||||
{
|
||||
// Colliding with a capsule
|
||||
|
||||
float capsuleHalfHeight = localCollisionShapes[collision].halfHeight;
|
||||
float capsuleRadius = localCollisionShapes[collision].radius;
|
||||
float capsuleMargin = localCollisionShapes[collision].margin;
|
||||
|
||||
float4x4 worldTransform = localCollisionShapes[collision].shapeTransform;
|
||||
|
||||
float4 c1 = float4(0.f, -capsuleHalfHeight, 0.f, 1.f);
|
||||
float4 c2 = float4(0.f, +capsuleHalfHeight, 0.f, 1.f);
|
||||
float4 worldC1 = mul(worldTransform, c1);
|
||||
float4 worldC2 = mul(worldTransform, c2);
|
||||
float3 segment = (worldC2 - worldC1).xyz;
|
||||
|
||||
// compute distance of tangent to vertex along line segment in capsule
|
||||
float distanceAlongSegment = -( dot( (worldC1 - position).xyz, segment ) / dot(segment, segment) );
|
||||
|
||||
float4 closestPoint = (worldC1 + float4(segment * distanceAlongSegment, 0.f));
|
||||
float distanceFromLine = length(position - closestPoint);
|
||||
float distanceFromC1 = length(worldC1 - position);
|
||||
float distanceFromC2 = length(worldC2 - position);
|
||||
|
||||
// Final distance from collision, point to push from, direction to push in
|
||||
// for impulse force
|
||||
float dist;
|
||||
float3 normalVector;
|
||||
if( distanceAlongSegment < 0 )
|
||||
{
|
||||
dist = distanceFromC1;
|
||||
normalVector = normalize(position - worldC1).xyz;
|
||||
} else if( distanceAlongSegment > 1.f ) {
|
||||
dist = distanceFromC2;
|
||||
normalVector = normalize(position - worldC2).xyz;
|
||||
} else {
|
||||
dist = distanceFromLine;
|
||||
normalVector = normalize(position - closestPoint).xyz;
|
||||
}
|
||||
|
||||
float3 colliderLinearVelocity = localCollisionShapes[collision].linearVelocity.xyz;
|
||||
float3 colliderAngularVelocity = localCollisionShapes[collision].angularVelocity.xyz;
|
||||
float3 velocityOfSurfacePoint = colliderLinearVelocity + cross(colliderAngularVelocity, position.xyz - worldTransform._m03_m13_m23);
|
||||
|
||||
float minDistance = capsuleRadius + capsuleMargin;
|
||||
|
||||
// In case of no collision, this is the value of velocity
|
||||
velocity = (position - previousPosition).xyz * velocityCoefficient * isolverdt;
|
||||
|
||||
|
||||
// Check for a collision
|
||||
if( dist < minDistance )
|
||||
{
|
||||
// Project back to surface along normal
|
||||
position = position + float4((minDistance - dist)*normalVector*0.9, 0.f);
|
||||
velocity = (position - previousPosition).xyz * velocityCoefficient * isolverdt;
|
||||
float3 relativeVelocity = velocity - velocityOfSurfacePoint;
|
||||
|
||||
float3 p1 = normalize(cross(normalVector, segment));
|
||||
float3 p2 = normalize(cross(p1, normalVector));
|
||||
// Full friction is sum of velocities in each direction of plane
|
||||
float3 frictionVector = p1*dot(relativeVelocity, p1) + p2*dot(relativeVelocity, p2);
|
||||
|
||||
// Real friction is peak friction corrected by friction coefficients
|
||||
frictionVector = frictionVector * (colliderFriction*clothFriction);
|
||||
|
||||
float approachSpeed = dot(relativeVelocity, normalVector);
|
||||
|
||||
if( approachSpeed <= 0.0 )
|
||||
forceOnVertex -= frictionVector;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Update velocity
|
||||
float3 difference = position.xyz - previousPosition.xyz;
|
||||
velocity = difference*velocityCoefficient*isolverdt;
|
||||
}
|
||||
|
||||
g_vertexVelocities[nodeID] = float4(velocity, 0.f);
|
||||
|
||||
// Update external force
|
||||
g_vertexForces[nodeID] = float4(forceOnVertex, 0.f);
|
||||
|
||||
g_vertexPositions[nodeID] = float4(position.xyz, 0.f);
|
||||
}
|
||||
|
||||
);
|
||||
Reference in New Issue
Block a user