#pragma kernel ApplyInertialForces #pragma kernel ApplyRigidbodyDeltas #pragma kernel StoreStartState #pragma kernel PredictPositions #pragma kernel UpdateVelocities #pragma kernel UpdatePositions #pragma kernel Interpolate #include "Bounds.cginc" #include "Integration.cginc" #include "CollisionMaterial.cginc" #include "SolverParameters.cginc" #include "MathUtils.cginc" #include "Rigidbody.cginc" StructuredBuffer simplices; StructuredBuffer activeParticles; StructuredBuffer invMasses; StructuredBuffer invRotationalMasses; StructuredBuffer phases; StructuredBuffer buoyancies; StructuredBuffer fluidRadii; StructuredBuffer R_startPositions; StructuredBuffer R_endPositions; StructuredBuffer R_startOrientations; StructuredBuffer R_endOrientations; RWStructuredBuffer positions; RWStructuredBuffer orientations; RWStructuredBuffer principalRadii; RWStructuredBuffer startPositions; RWStructuredBuffer endPositions; RWStructuredBuffer startOrientations; RWStructuredBuffer endOrientations; RWStructuredBuffer renderablePositions; RWStructuredBuffer renderableOrientations; RWStructuredBuffer renderableRadii; RWStructuredBuffer prevPositions; RWStructuredBuffer prevOrientations; RWStructuredBuffer velocities; RWStructuredBuffer angularVelocities; RWStructuredBuffer externalForces; RWStructuredBuffer externalTorques; RWStructuredBuffer linearDeltas; RWStructuredBuffer angularDeltas; // Variables set from the CPU uint particleCount; float deltaTime; float blendFactor; float velocityScale; float4 angularVel; float4 inertialAccel; float4 eulerAccel; [numthreads(128, 1, 1)] void ApplyInertialForces(uint3 id : SV_DispatchThreadID) { unsigned int i = id.x; if (i >= particleCount) return; int p = activeParticles[i]; if (invMasses[p] > 0) { float4 euler = float4(cross(eulerAccel.xyz, positions[p].xyz), 0); float4 centrifugal = float4(cross(angularVel.xyz, cross(angularVel.xyz, positions[p].xyz)), 0); float4 coriolis = 2 * float4(cross(angularVel.xyz, velocities[p].xyz), 0); float4 angularAccel = euler + coriolis + centrifugal; velocities[p] -= (inertialAccel * worldLinearInertiaScale + angularAccel * worldAngularInertiaScale) * deltaTime; } } [numthreads(128, 1, 1)] void ApplyRigidbodyDeltas (uint3 id : SV_DispatchThreadID) { unsigned int i = id.x; if (i >= particleCount) return; linearDeltas[i].xyz = float3(asfloat(linearDeltasAsInt[i].x), asfloat(linearDeltasAsInt[i].y), asfloat(linearDeltasAsInt[i].z)); angularDeltas[i].xyz = float3(asfloat(angularDeltasAsInt[i].x), asfloat(angularDeltasAsInt[i].y), asfloat(angularDeltasAsInt[i].z)); } [numthreads(128, 1, 1)] void StoreStartState (uint3 id : SV_DispatchThreadID) { unsigned int i = id.x; if (i >= particleCount) return; startPositions[i] = endPositions[i]; startOrientations[i] = endOrientations[i]; endPositions[i] = positions[i]; endOrientations[i] = orientations[i]; } [numthreads(128, 1, 1)] void PredictPositions (uint3 id : SV_DispatchThreadID) { unsigned int i = id.x; if (i >= particleCount) return; int p = activeParticles[i]; // the previous position/orientation is the current position/orientation at the start of the step. prevPositions[p] = positions[p]; prevOrientations[p] = orientations[p]; // predict positions: if (invMasses[p] > 0) { float4 effectiveGravity = float4(gravity,0); // Adjust gravity for buoyant fluid particles: if ((phases[p] & (int)PHASE_FLUID) != 0) effectiveGravity *= -buoyancies[p].z; // apply external forces and gravity: float4 vel = velocities[p] + (invMasses[p] * externalForces[p] + effectiveGravity) * deltaTime; // project velocity to 2D plane if needed: if (mode == 1) vel[3] = 0; velocities[p] = vel; } if (invRotationalMasses[p] > 0) { // apply external torques (simplification: we don't use full inertia tensor here) float3 angularVel = angularVelocities[p].xyz + invRotationalMasses[p] * externalTorques[p].xyz * deltaTime; // project angular velocity to 2D plane normal if needed: if (mode == 1) angularVel = Project(angularVel,float3(0, 0, 1)); angularVelocities[p] = float4(angularVel, angularVelocities[p].w); } positions[p] = IntegrateLinear(positions[p], velocities[p], deltaTime); orientations[p] = IntegrateAngular(orientations[p], angularVelocities[p], deltaTime); } [numthreads(128, 1, 1)] void UpdateVelocities (uint3 id : SV_DispatchThreadID) { unsigned int i = id.x; if (i >= particleCount) return; int p = activeParticles[i]; // Project particles on the XY plane if we are in 2D mode: if (mode == 1) { // restrict position to the 2D plane float4 pos = positions[p]; pos[2] = prevPositions[p][2]; positions[p] = pos; } if (invMasses[p] > 0) velocities[p] = DifferentiateLinear(positions[p],prevPositions[p],deltaTime); else velocities[p] = FLOAT4_ZERO; if (invRotationalMasses[p] > 0) angularVelocities[p].xyz = DifferentiateAngular(orientations[p], prevOrientations[p], deltaTime).xyz; else angularVelocities[p] = FLOAT4_ZERO; } [numthreads(128, 1, 1)] void UpdatePositions (uint3 id : SV_DispatchThreadID) { unsigned int i = id.x; if (i >= particleCount) return; int p = activeParticles[i]; // damp velocities: velocities[p] *= velocityScale; angularVelocities[p].xyz *= velocityScale; // clamp velocities: float velMagnitude = length(velocities[p]); float angularVelMagnitude = length(angularVelocities[p].xyz); if (velMagnitude > EPSILON) velocities[p] *= min(maxVelocity, velMagnitude) / velMagnitude; if (angularVelMagnitude > EPSILON) angularVelocities[p].xyz *= min(maxAngularVelocity, angularVelMagnitude) / angularVelMagnitude; // if the kinetic energy is below the sleep threshold, keep the particle at its previous position. if (velMagnitude * velMagnitude * 0.5f + angularVelMagnitude * angularVelMagnitude * 0.5f <= sleepThreshold) { positions[p] = prevPositions[p]; orientations[p] = prevOrientations[p]; velocities[p] = FLOAT4_ZERO; angularVelocities[p].xyz = float3(0,0,0); } } [numthreads(128, 1, 1)] void Interpolate (uint3 id : SV_DispatchThreadID) { unsigned int i = id.x; if (i >= particleCount) return; if (interpolation == 1) { renderablePositions[i] = lerp(R_startPositions[i], R_endPositions[i], blendFactor); renderableOrientations[i] = normalize(q_slerp(R_startOrientations[i], R_endOrientations[i], blendFactor)); renderableRadii[i] = principalRadii[i]; } else if (interpolation == 2) { renderablePositions[i] = lerp(R_endPositions[i], positions[i], blendFactor); renderableOrientations[i] = normalize(q_slerp(R_endOrientations[i], orientations[i], blendFactor)); renderableRadii[i] = principalRadii[i]; } else { renderablePositions[i] = R_endPositions[i]; renderableOrientations[i] = normalize(R_endOrientations[i]); renderableRadii[i] = principalRadii[i]; } }