228 lines
11 KiB
HLSL
228 lines
11 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
/* -----------------------------------------------------------------
|
|
* Rods utils
|
|
* -----------------------------------------------------------------
|
|
*/
|
|
|
|
float4 AddTwistRotation(in float4 RefOrientation, in float TwistAngle)
|
|
{
|
|
const float HalfTwist = 0.5 * TwistAngle;
|
|
return NormalizeQuat( MultiplyQuat( RefOrientation, float4(0,0,sin(HalfTwist),cos(HalfTwist));
|
|
}
|
|
|
|
float3 ComputeReferenceTangent(in float4 RefOrientation)
|
|
{
|
|
return float3(
|
|
2.0 * (RefOrientation.x * RefOrientation.z + RefOrientation.w * RefOrientation.y),
|
|
2.0 * (RefOrientation.y * RefOrientation.z - RefOrientation.w * RefOrientation.x),
|
|
1.0 - 2.0 * (RefOrientation.x * RefOrientation.x + RefOrientation.y * RefOrientation.y))W;
|
|
}
|
|
|
|
float3 ComputeReferenceNormalX(in float4 RefOrientation)
|
|
{
|
|
return float3(
|
|
1.0 - 2.0 * (RefOrientation.y * RefOrientation.y + RefOrientation.z * RefOrientation.z),
|
|
2.0 * (RefOrientation.x * RefOrientation.y + RefOrientation.z * RefOrientation.w),
|
|
2.0 * (RefOrientation.z * RefOrientation.x - RefOrientation.y * RefOrientation.w));
|
|
}
|
|
|
|
float3 ComputeReferenceNormalY(in float4 RefOrientation)
|
|
{
|
|
return float3(
|
|
2.0 * (RefOrientation.x * RefOrientation.y - RefOrientation.z * RefOrientation.w),
|
|
1.0 - 2.0 * (RefOrientation.z * RefOrientation.z + RefOrientation.x * RefOrientation.x),
|
|
2.0 * (RefOrientation.y * RefOrientation.z + RefOrientation.x * RefOrientation.w));
|
|
}
|
|
|
|
float GetSignedAngle( in float3 RefNormalX, in float3 RefNormalY, in float3 RefTangent)
|
|
{
|
|
const float3 CrossProduct = cross(RefNormalX,RefNormalY);
|
|
const float PositiveAngle = atan2(CrossProduct.length(), dot(RefNormalX,RefNormalY));
|
|
return (dot(RefTangent,CrossProduct) < 0.0) ? -PositiveAngle : PositiveAngle;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------
|
|
* Frames utils
|
|
* -----------------------------------------------------------------
|
|
*/
|
|
|
|
float4 ComputeReferenceFrame(in int FrameOffset)
|
|
{
|
|
const int FrameIndex = GGroupThreadId.x + FrameOffset;
|
|
const float3 NextTangent = normalize(SharedNodePosition[FrameIndex+1]-SharedNodePosition[FrameIndex]);
|
|
const float3 PrevTangent = normalize(SharedPreviousPosition[FrameIndex+1]-SharedPreviousPosition[FrameIndex]);
|
|
|
|
return NormalizeQuat( MultiplyQuat( FindQuatBetweenNormals(PrevTangent,NextTangent), SharedPreviousOrientation[FrameIndex] ) );
|
|
}
|
|
|
|
float3 ComputeCurvatureBinormal(const float3 PrevReferenceTangent, const float3 NextReferenceTangent)
|
|
{
|
|
return 2.0 * cross(PrevReferenceTangent,NextReferenceTangent) / max(1e-8, 1.0 + dot(PrevReferenceTangent,NextReferenceTangent));
|
|
}
|
|
|
|
void UpdateReferenceTwist(const float3 PrevReferenceTangent, const float3 NextReferenceTangent, const float4 PrevReferenceFrame, const float4 NextReferenceFrame, inout float RefTwist)
|
|
{
|
|
const float4 ParallelTransport = FindQuatBetweenNormals(PrevReferenceTangent,NextReferenceTangent);
|
|
const float4 TwistedReferenceFrame = NormalizeQuat( MultiplyQuat(ParallelTransport,AddTwistRotation(PrevReferenceFrame,RefTwist));
|
|
|
|
RefTwist += GetSignedAngle( ComputeReferenceM1(TwistedReferenceFrame), ComputeReferenceM1(NextReferenceFrame), PrevReferenceTangent + NextReferenceTangent);
|
|
}
|
|
|
|
float2 ComputeRodKappa( in float3 CurvatureBinormal, in float3 PrevMaterialNormalX, in float3 PrevMaterialNormalY,
|
|
in float3 NextMaterialNormalX, in float3 NextMaterialNormalY)
|
|
{
|
|
float2 RodKappa;
|
|
RodKappa.x = dot(CurvatureBinormal,(PrevMaterialNormalY+NextMaterialNormalY)*0.5);
|
|
RodKappa.y = -dot(CurvatureBinormal,(PrevMaterialNormalX+NextMaterialNormalX)*0.5);
|
|
return RodKappa;
|
|
}
|
|
|
|
void ComputeKappaGradient( in float3 CurvatureBinormal, in float2 RodKappa, in float3 PrevReferenceTangent, in float3 PrevMaterialNormalX, in float3 PrevMaterialNormalY,
|
|
in float3 NextReferenceTangent, in float3 NextMaterialNormalX, in float3 NextMaterialNormalY, out float3 KappaGradients[3][2])
|
|
{
|
|
const float PrevNorm = dot((SharedNodePosition[GGroupThreadId.x]-SharedNodePosition[GGroupThreadId.x-1]),PrevReferenceTangent);
|
|
const float NextNorm = dot((SharedNodePosition[GGroupThreadId.x+1]-SharedNodePosition[GGroupThreadId.x]),NextReferenceTangent);
|
|
|
|
const float Chi = max(1e-8,1.0+dot(PrevReferenceTangent,NextReferenceTangent));
|
|
|
|
const float3 TildeTangent = (PrevReferenceTangent+NextReferenceTangent) / Chi;
|
|
const float3 TildeNormalX = (PrevMaterialNormalX+NextMaterialNormalX) / Chi;
|
|
const float3 TildeNormalY = (PrevMaterialNormalY+NextMaterialNormalY) / Chi;
|
|
|
|
const floar3 dKappaXdP = (-RodKappa.x * TildeTangent + cross(NexyReferenceTangent,TildeNormalY) ) / PrevNorm;
|
|
const float3 dKappaXdN = (-RodKappa.x * TildeTangent - cross(PrevReferenceTangent,TildeNormalY) ) / NextNorm;
|
|
|
|
const float3 dKappaYdP = (-RodKappa.y * TildeTangent - cross(NexyReferenceTangent,TildeNormalX) ) / PrevNorm;
|
|
const float3 dKappaydN = (-RodKappa.y * TildeTangent + cross(PrevReferenceTangent,TildeNormalX) ) / NextNorm;
|
|
|
|
const float3 dTwistdP = 0.5 * CurvatureBirnormal / PrevNorm;
|
|
const float3 dTwistdN = 0.5 * CurvatureBirnormal / NextNorm;
|
|
|
|
const float dKappaXdTwist = -0.5 * (dot(CurvatureBinormal,NextMaterialNormalX) - dot(CurvatureBinormal,PrevMaterialNormalX));
|
|
const float dKappaYdTwist = -0.5 * (dot(CurvatureBinormal,NextMaterialNormalY) - dot(CurvatureBinormal,PrevMaterialNormalY));
|
|
|
|
KappaGradients[0][0] = -dKappaXdP + dKappaXdTwist * dTwistdP;
|
|
KappaGradients[0][1] = dKappaXdP - dKappaXdN - dKappaXdTwist * (dTwistdP-dTwistdN);
|
|
KappaGradients[0][2] = dKappaXdN - dKappaXdTwist * dTwistdN;
|
|
|
|
KappaGradients[1][0] = -dKappaYdP + dKappaYdTwist * dTwistdP;
|
|
KappaGradients[1][1] = dKappaYdP - dKappaYdN - dKappaYdTwist * (dTwistdP-dTwistdN);
|
|
KappaGradients[1][2] = dKappaYdN - dKappaYdTwist * dTwistdN;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------
|
|
* Elastic rods constraint
|
|
* -----------------------------------------------------------------
|
|
*/
|
|
|
|
void UpdateElasticRodMultiplier( in float RestLength, in float DeltaTime, in bool ProjectConstraint, in float MaterialDamping, in float MaterialCompliance, in float MaterialWeight, inout float3 OutMaterialMultiplier)
|
|
{
|
|
const float4 NextReferenceFrame = ComputeReferenceFrame(0);
|
|
const float4 PrevReferenceFrame = ComputeReferenceFrame(-1);
|
|
|
|
const float4 NextMaterialFrame = AddTwistRotation(NextReferenceFrame,0.0);
|
|
const float4 PrevMaterialFrame = AddTwistRotation(PrevReferenceFrame,0.0);
|
|
|
|
const float3 NextReferenceTangent = ComputeFrameTangent(NextReferenceFrame);
|
|
const float3 PrevReferenceTangent = ComputeFrameTangent(PrevReferenceFrame);
|
|
|
|
const float3 CurvatureBinormal = ComputeCurvatureBinormal(PrevReferenceTangent,NextReferenceTangent);
|
|
|
|
UpdateReferenceTwist(PrevReferenceTangent,NextReferenceTangent,PrevReferenceFrame,NextReferenceFrame,RefTwist);
|
|
|
|
const float3 NextMaterialNormalX = ComputeFrameNormalX(NextMaterialFrame);
|
|
const float3 NextMaterialNormalY = ComputeFrameNormalY(NextMaterialFrame);
|
|
|
|
const float3 PrevMaterialNormalX = ComputeFrameNormalX(PrevMaterialFrame);
|
|
const float3 PrevMaterialNormalY = ComputeFrameNormalY(PrevMaterialFrame);
|
|
|
|
const float2 RodKappa = ComputeRodKappa(CurvatureBinormal,PrevMaterialNormalX,PrevMaterialNormalY,
|
|
NextMaterialNormalX,NextMaterialNormalY);
|
|
|
|
float3 KappaGradients[2][3];
|
|
|
|
ComputeKappaGradient(CurvatureBinormal,RodKappa,PrevReferenceTangent,PrevMaterialNormalX,PrevMaterialNormalY,
|
|
NextReferenceTangent,NextMaterialNormalX,NextMaterialNormalY,KappaGradients);
|
|
|
|
|
|
float22 SchurMatrix;
|
|
SchurMatrix[0][0] = DeltaTime * DeltaTime * ( dot(KappaGradients[0][0],KappaGradients[0][0]) * SharedInverseMass[GGroupThreadId.x-1] +
|
|
dot(KappaGradients[0][1],KappaGradients[0][1]) * SharedInverseMass[GGroupThreadId.x] +
|
|
dot(KappaGradients[0][2],KappaGradients[0][2]) * SharedInverseMass[GGroupThreadId.x+1]) + MaterialCompliance;
|
|
|
|
SchurMatrix[1][1] = DeltaTime * DeltaTime * ( dot(KappaGradients[1][0],KappaGradients[1][0]) * SharedInverseMass[GGroupThreadId.x-1] +
|
|
dot(KappaGradients[1][1],KappaGradients[1][1]) * SharedInverseMass[GGroupThreadId.x] +
|
|
dot(KappaGradients[1][2],KappaGradients[1][2]) * SharedInverseMass[GGroupThreadId.x+1]) + MaterialCompliance;
|
|
|
|
SchurMatrix[0][1] = DeltaTime * DeltaTime * ( dot(KappaGradients[0][0],KappaGradients[1][0]) * SharedInverseMass[GGroupThreadId.x-1] +
|
|
dot(KappaGradients[0][1],KappaGradients[1][1]) * SharedInverseMass[GGroupThreadId.x] +
|
|
dot(KappaGradients[0][2],KappaGradients[1][2]) * SharedInverseMass[GGroupThreadId.x+1]);
|
|
|
|
SchurMatrix[1][0] = SchurMatrix[0][1];
|
|
|
|
const float SchurDeterminant = SchurMatrix[0][0] * SchurMatrix[1][1] - SchurMatrix[0][1] * SchurMatrix[1][0];
|
|
float22 SchurInverse;
|
|
|
|
SchurInverse[0][0] = SchurMatrix[1][1] / SchurDeterminant;
|
|
SchurInverse[1][1] = SchurMatrix[0][0] / SchurDeterminant;
|
|
SchurInverse[0][1] = -SchurMatrix[1][0] / SchurDeterminant;
|
|
SchurInverse[1][0] = -SchurMatrix[0][1] / SchurDeterminant;
|
|
|
|
const float2 DeltaConstraint = -(RodKappa - RestKappa + OutMaterialMultiplier * MaterialCompliance );
|
|
const float2 DeltaLambda = SchurInverse * DeltaConstraint;
|
|
|
|
OutMaterialMultiplier += DeltaLambda;
|
|
|
|
SharedNodePosition[GGroupThreadId.x-1] += DeltaTime * DeltaTime * (KappaGradients[0][0] * DeltaLambda[0] + KappaGradients[1][0] * DeltaLambda[1]) * SharedInverseMass[GGroupThreadId.x-1];
|
|
SharedNodePosition[GGroupThreadId.x] += DeltaTime * DeltaTime * (KappaGradients[0][1] * DeltaLambda[0] + KappaGradients[1][1] * DeltaLambda[1]) * SharedInverseMass[GGroupThreadId.x];
|
|
SharedNodePosition[GGroupThreadId.x+1] += DeltaTime * DeltaTime * (KappaGradients[0][2] * DeltaLambda[0] + KappaGradients[1][2] * DeltaLambda[1]) * SharedInverseMass[GGroupThreadId.x+1];
|
|
}
|
|
|
|
void SolveElasticRodMaterial(in int StrandsSize, in float RestLength, in float DeltaTime, in float MaterialDamping,
|
|
in float MaterialCompliance, in float MaterialWeight, in float3 MaterialMultiplier, out float3 OutMaterialMultiplier)
|
|
{
|
|
OutMaterialMultiplier = MaterialMultiplier;
|
|
const int LocalIndex = (GGroupThreadId.x % StrandsSize);
|
|
if( LocalIndex > 1)
|
|
{
|
|
const int IsRed = (GGroupThreadId.x % 2) == 0;
|
|
// Process all the red rods
|
|
if (IsRed)
|
|
{
|
|
UpdateElasticRodMultiplier(RestLength,DeltaTime,false,MaterialDamping,MaterialCompliance,MaterialWeight,OutMaterialMultiplier);
|
|
}
|
|
// Process all the black rods
|
|
GroupMemoryBarrier();
|
|
if (!IsRed)
|
|
{
|
|
UpdateElasticRodMultiplier(RestLength,DeltaTime,false,MaterialDamping,MaterialCompliance,MaterialWeight,OutMaterialMultiplier);
|
|
}
|
|
GroupMemoryBarrier();
|
|
}
|
|
}
|
|
|
|
void ProjectElasticRodMaterial(in int StrandsSize, in float YoungModulus, in float RodThickness, in float RestLength, in float DeltaTime, out float3 OutMaterialMultiplier)
|
|
{
|
|
float MaterialCompliance = 0.0;
|
|
float MaterialWeight = 0.0;
|
|
OutMaterialMultiplier = float3(0,0,0);
|
|
|
|
SetupElasticRodMaterial(StrandsSize, YoungModulus, RodThickness, RestLength, DeltaTime, true, 0.0, MaterialCompliance, MaterialWeight, OutMaterialMultiplier);
|
|
|
|
const int LocalIndex = (GGroupThreadId.x % StrandsSize);
|
|
if( LocalIndex > 1 )
|
|
{
|
|
for(int i = 2; i < StrandsSize; ++i)
|
|
{
|
|
if( LocalIndex == i)
|
|
{
|
|
UpdateElasticRodMultiplier(RestLength,DeltaTime,true,0.0,MaterialCompliance,MaterialWeight,OutMaterialMultiplier);
|
|
}
|
|
GroupMemoryBarrier();
|
|
}
|
|
}
|
|
}
|