#pragma kernel Project #pragma kernel Apply #include "ContactHandling.cginc" #include "Integration.cginc" #include "CollisionMaterial.cginc" #include "Simplex.cginc" #include "AtomicDeltas.cginc" StructuredBuffer particleIndices; StructuredBuffer simplices; StructuredBuffer prevPositions; StructuredBuffer prevOrientations; StructuredBuffer invMasses; StructuredBuffer invRotationalMasses; StructuredBuffer principalRadii; RWStructuredBuffer positions; RWStructuredBuffer orientations; RWStructuredBuffer deltas; RWStructuredBuffer particleContacts; RWStructuredBuffer effectiveMasses; StructuredBuffer dispatchBuffer; // Variables set from the CPU uint particleCount; float substepTime; float stepTime; float sorFactor; [numthreads(128, 1, 1)] void Project (uint3 id : SV_DispatchThreadID) { unsigned int i = id.x; if (i >= dispatchBuffer[3]) return; int simplexSizeA; int simplexSizeB; int simplexStartA = GetSimplexStartAndSize(particleContacts[i].bodyA, simplexSizeA); int simplexStartB = GetSimplexStartAndSize(particleContacts[i].bodyB, simplexSizeB); // Combine collision materials (use material from first particle in simplex) collisionMaterial material = CombineCollisionMaterials(collisionMaterialIndices[simplices[simplexStartA]], collisionMaterialIndices[simplices[simplexStartB]]); float4 prevPositionA = float4(0,0,0,0); float4 linearVelocityA = float4(0,0,0,0); float4 angularVelocityA = float4(0,0,0,0); float invRotationalMassA = 0; quaternion orientationA = quaternion(0, 0, 0, 0); float simplexRadiusA = 0; float4 prevPositionB = float4(0,0,0,0); float4 linearVelocityB = float4(0,0,0,0); float4 angularVelocityB = float4(0,0,0,0); float invRotationalMassB = 0; quaternion orientationB = quaternion(0, 0, 0, 0); float simplexRadiusB = 0; int j = 0; for (j = 0; j < simplexSizeA; ++j) { int particleIndex = simplices[simplexStartA + j]; prevPositionA += prevPositions[particleIndex] * particleContacts[i].pointA[j]; linearVelocityA += DifferentiateLinear(positions[particleIndex], prevPositions[particleIndex], substepTime) * particleContacts[i].pointA[j]; angularVelocityA += DifferentiateAngular(orientations[particleIndex], prevOrientations[particleIndex], substepTime) * particleContacts[i].pointA[j]; invRotationalMassA += invRotationalMasses[particleIndex] * particleContacts[i].pointA[j]; orientationA += orientations[particleIndex] * particleContacts[i].pointA[j]; simplexRadiusA += EllipsoidRadius(particleContacts[i].normal, prevOrientations[particleIndex], principalRadii[particleIndex].xyz) * particleContacts[i].pointA[j]; } for (j = 0; j < simplexSizeB; ++j) { int particleIndex = simplices[simplexStartB + j]; prevPositionB += prevPositions[particleIndex] * particleContacts[i].pointB[j]; linearVelocityB += DifferentiateLinear(positions[particleIndex], prevPositions[particleIndex], substepTime) * particleContacts[i].pointB[j]; angularVelocityB += DifferentiateAngular(orientations[particleIndex], prevOrientations[particleIndex], substepTime) * particleContacts[i].pointB[j]; invRotationalMassB += invRotationalMasses[particleIndex] * particleContacts[i].pointB[j]; orientationB += orientations[particleIndex] * particleContacts[i].pointB[j]; simplexRadiusB += EllipsoidRadius(particleContacts[i].normal, prevOrientations[particleIndex], principalRadii[particleIndex].xyz) * particleContacts[i].pointB[j]; } float4 rA = FLOAT4_ZERO; float4 rB = FLOAT4_ZERO; // Consider angular velocities if rolling contacts are enabled: if (material.rollingContacts > 0) { rA = -particleContacts[i].normal * simplexRadiusA; rB = particleContacts[i].normal * simplexRadiusB; linearVelocityA += float4(cross(angularVelocityA.xyz, rA.xyz), 0); linearVelocityB += float4(cross(angularVelocityB.xyz, rB.xyz), 0); } // Calculate relative velocity: float4 relativeVelocity = linearVelocityA - linearVelocityB; // Determine impulse magnitude: float tangentMass = effectiveMasses[i].tangentInvMassA + effectiveMasses[i].tangentInvMassB; float bitangentMass = effectiveMasses[i].bitangentInvMassA + effectiveMasses[i].bitangentInvMassB; float2 impulses = SolveFriction(particleContacts[i],tangentMass,bitangentMass,relativeVelocity, material.staticFriction, material.dynamicFriction, stepTime); if (abs(impulses.x) > EPSILON || abs(impulses.y) > EPSILON) { float4 tangentImpulse = impulses.x * particleContacts[i].tangent; float4 bitangentImpulse = impulses.y * GetBitangent(particleContacts[i]); float4 totalImpulse = tangentImpulse + bitangentImpulse; float baryScale = BaryScale(particleContacts[i].pointA); for (j = 0; j < simplexSizeA; ++j) { int particleIndex = simplices[simplexStartA + j]; float4 delta1 = (tangentImpulse * effectiveMasses[i].tangentInvMassA + bitangentImpulse * effectiveMasses[i].bitangentInvMassA) * substepTime * particleContacts[i].pointA[j] * baryScale; AtomicAddPositionDelta(particleIndex, delta1); } baryScale = BaryScale(particleContacts[i].pointB); for (j = 0; j < simplexSizeB; ++j) { int particleIndex = simplices[simplexStartB + j]; float4 delta2 = -(tangentImpulse * effectiveMasses[i].tangentInvMassB + bitangentImpulse * effectiveMasses[i].bitangentInvMassB) * substepTime * particleContacts[i].pointB[j] * baryScale; AtomicAddPositionDelta(particleIndex, delta2); } // Rolling contacts: if (material.rollingContacts > 0) { float4 invInertiaTensorA = 1.0/(GetParticleInertiaTensor(simplexRadiusA, invRotationalMassA) + FLOAT4_EPSILON); float4 invInertiaTensorB = 1.0/(GetParticleInertiaTensor(simplexRadiusB, invRotationalMassB) + FLOAT4_EPSILON); // Calculate angular velocity deltas due to friction impulse: float4x4 solverInertiaA = TransformInertiaTensor(invInertiaTensorA, orientationA); float4x4 solverInertiaB = TransformInertiaTensor(invInertiaTensorB, orientationB); float4 angVelDeltaA = mul(solverInertiaA, float4(cross(rA.xyz, totalImpulse.xyz), 0)); float4 angVelDeltaB = -mul(solverInertiaB, float4(cross(rB.xyz, totalImpulse.xyz), 0)); // Final angular velocities, after adding the deltas: angularVelocityA += angVelDeltaA; angularVelocityB += angVelDeltaB; // Calculate weights (inverse masses): float invMassA = length(mul(solverInertiaA, normalizesafe(angularVelocityA))); float invMassB = length(mul(solverInertiaB, normalizesafe(angularVelocityB))); // Calculate rolling axis and angular velocity deltas: float4 rollAxis = FLOAT4_ZERO; float rollingImpulse = SolveRollingFriction(particleContacts[i],angularVelocityA, angularVelocityB, material.rollingFriction, invMassA, invMassB, rollAxis); angVelDeltaA += rollAxis * rollingImpulse * invMassA; angVelDeltaB -= rollAxis * rollingImpulse * invMassB; // Apply orientation deltas to particles: quaternion orientationDeltaA = AngularVelocityToSpinQuaternion(orientationA, angVelDeltaA, substepTime); quaternion orientationDeltaB = AngularVelocityToSpinQuaternion(orientationB, angVelDeltaB, substepTime); for (j = 0; j < simplexSizeA; ++j) { int particleIndex = simplices[simplexStartA + j]; AtomicAddOrientationDelta(particleIndex, orientationDeltaA); } for (j = 0; j < simplexSizeB; ++j) { int particleIndex = simplices[simplexStartB + j]; AtomicAddOrientationDelta(particleIndex, orientationDeltaB); } } } } [numthreads(128, 1, 1)] void Apply (uint3 id : SV_DispatchThreadID) { unsigned int threadIndex = id.x; if (threadIndex >= particleCount) return; int p = particleIndices[threadIndex]; ApplyPositionDelta(positions, p, sorFactor); ApplyOrientationDelta(orientations, p, sorFactor); }