Files
XCEngine/engine/third_party/physx/source/gpusimulationcontroller/src/CUDA/FEMClothUtil.cuh

705 lines
22 KiB
Plaintext

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef __CU_FEMCLOTHUTIL_CUH__
#define __CU_FEMCLOTHUTIL_CUH__
#include "PxgFEMCloth.h"
#include "vector_types.h"
#include "foundation/PxVec3.h"
#include "foundation/PxVec4.h"
#include "foundation/PxBounds3.h"
#include "copy.cuh"
#include "shuffle.cuh"
#include "assert.h"
#include "stdio.h"
#include "PxgFEMClothCoreKernelIndices.h"
#include "atomic.cuh"
#include "PxsDeformableSurfaceMaterialCore.h"
#include "femMidphaseScratch.cuh"
#include "GuBV32.h"
#include "deformableUtils.cuh"
#include "particleSystem.cuh"
#include "utils.cuh"
using namespace physx;
/*******************************************************************************
*
*
* Definitions
*
*
******************************************************************************/
#define FEMCLOTH_SQRT2 1.4142135623730950488016887242097f
#define FEMCLOTH_SQRT3 1.7320508075688772935274463415059f
#define FEMCLOTH_THRESHOLD 1.0e-14f
#define FEMCLOTH_PI 3.14159265358979323846f
#define FEMCLOTH_HALF_PI 1.57079632679489661923f
#define FEMCLOTH_2PI 6.28318530717958647692f
#define FEMCLOTH_2PI_INV 0.15915494309189533576888376337251f
/*******************************************************************************
*
*
* Math functions
*
*
******************************************************************************/
//!
//! \brief : Extract rotation R [r0, r1] from F [f0, f1] in 2D
//! \reference: https://en.wikipedia.org/wiki/Square_root_of_a_2_by_2_matrix
//!
static PX_FORCE_INLINE __device__ void extractRotation2D(PxVec2& r0, PxVec2& r1, const PxVec2& f0, const PxVec2& f1)
{
// R: rotation part of F (by polar decopmosition)
// F^T * F = [S2[0], S2[2]]
// [S2[2], S2[1]]
const PxVec3 S2(f0.dot(f0), f1.dot(f1), f0.dot(f1));
const float det = S2[0] * S2[1] - S2[2] * S2[2];
if (det < FEMCLOTH_THRESHOLD)
{
r0.x = 1.0f;
r0.y = 0.0f;
r1.x = 0.0f;
r1.y = 1.0f;
return;
}
const float s0 = sqrtf(det);
const float t = sqrtf(S2[0] + S2[1] + 2.0f * s0);
assert(t > 0.0f);
if (t < FEMCLOTH_THRESHOLD)
{
r0.x = 1.0f;
r0.y = 0.0f;
r1.x = 0.0f;
r1.y = 1.0f;
return;
}
const float tInv = 1.0f / t;
PxVec3 S(S2);
S[0] += s0;
S[1] += s0;
S *= tInv;
const float sDet = S[0] * S[1] - S[2] * S[2];
assert(sDet > 0.0f);
if (sDet < FEMCLOTH_THRESHOLD)
{
r0.x = 1.0f;
r0.y = 0.0f;
r1.x = 0.0f;
r1.y = 1.0f;
return;
}
const float sDetInv = 1.0f / sDet;
PxVec3 SInv(S[1], S[0], -S[2]);
SInv *= sDetInv;
// R = [r0 r1]
r0 = SInv[0] * f0 + SInv[2] * f1;
r1 = SInv[2] * f0 + SInv[1] * f1;
}
//!
//! \brief : Approximated atan2: max error of ~1/10000
//! \reference: https://mazzo.li/posts/vectorized-atan2.html
//!
static PX_FORCE_INLINE __device__ PxReal atanApprox(PxReal x)
{
PxReal x2 = x * x;
return x * (0.99997726f + x2 * (-0.33262347f + x2 * (0.19354346f + x2 * (-0.11643287f + x2 * (0.05265332f + x2 * (-0.01172120f))))));
}
static PX_FORCE_INLINE __device__ PxReal atan2Approx(PxReal y, PxReal x)
{
bool swap = PxAbs(x) < PxAbs(y);
PxReal input = swap ? (x / y) : (y / x);
PxReal output = atanApprox(input);
output = swap ? (input >= 0.f ? FEMCLOTH_HALF_PI : -FEMCLOTH_HALF_PI) - output : output;
if(x < 0.f)
{
output += (y >= 0.f ? FEMCLOTH_PI : -FEMCLOTH_PI);
}
return output;
}
static PX_FORCE_INLINE __device__ bool velocityClamping(float4& pos, float4& vel, float4& accumDelta, PxReal maxVel, PxReal dt,
const float4& prevPos)
{
const PxReal maxVelSq = maxVel * maxVel;
const PxReal velMagSq = PxLoad3(vel).magnitudeSquared();
if (velMagSq > maxVelSq)
{
vel *= maxVel / PxSqrt(velMagSq);
float4 newPos = prevPos + vel * dt;
newPos.w = pos.w;
vel.w = pos.w;
const float4 delta = newPos - pos;
pos = newPos;
accumDelta += delta;
return true; // Velocity is clamped.
}
return false; // Velocity is not clamped.
}
/*******************************************************************************
*
*
* Delta lambda updates
*
*
******************************************************************************/
//!
//! \brief : Returns the delta lambda in XPBD when a constraint has three vertex degrees of freedom, applicable in both 2D and 3D but without damping.
//!
template <typename PxVec2Or3>
static PX_FORCE_INLINE __device__ float queryDeltaLambda(float C, const PxVec2Or3& dCdx0, const PxVec2Or3& dCdx1, const PxVec2Or3& dCdx2,
float alphaTilde, float lambda, float massInv0, float massInv1, float massInv2)
{
const float denom =
(massInv0 * dCdx0.magnitudeSquared() + massInv1 * dCdx1.magnitudeSquared() + massInv2 * dCdx2.magnitudeSquared()) + alphaTilde;
assert(denom != 0.0f);
if (denom < FEMCLOTH_THRESHOLD)
return 0.0f;
return (-C - alphaTilde * lambda) / denom;
}
//!
//! \brief : Returns the delta lambda in XPBD when a constraint has three vertex degrees of freedom, applicable in both 2D and 3D with damping.
//!
template <typename PxVec2Or3>
static PX_FORCE_INLINE __device__ float queryDeltaLambda(float C, const PxVec2Or3& dCdx0, const PxVec2Or3& dCdx1, const PxVec2Or3& dCdx2,
float alphaTilde, float lambda, float massInv0, float massInv1, float massInv2,
float damping, float dtInv, float dCdT)
{
const float denom = (1.0f + damping * dtInv) * (massInv0 * dCdx0.magnitudeSquared() + massInv1 * dCdx1.magnitudeSquared() +
massInv2 * dCdx2.magnitudeSquared()) +
alphaTilde;
assert(denom != 0.0f);
if (denom < FEMCLOTH_THRESHOLD)
return 0.0f;
return -(C + alphaTilde * lambda + damping * dCdT) / denom;
}
//!
//! \brief : Returns the delta lambda in XPBD when a constraint has four vertex degrees of freedom, applicable in 3D but without damping.
//!
static PX_FORCE_INLINE __device__ float queryDeltaLambda(float C, const PxVec3& dCdx0, const PxVec3& dCdx1, const PxVec3& dCdx2,
const PxVec3& dCdx3, float alphaTilde, float lambda, float massInv0,
float massInv1, float massInv2, float massInv3)
{
const float denom = (massInv0 * dCdx0.magnitudeSquared() + massInv1 * dCdx1.magnitudeSquared() + massInv2 * dCdx2.magnitudeSquared() +
massInv3 * dCdx3.magnitudeSquared()) +
alphaTilde;
assert(denom != 0.0f);
if (denom < FEMCLOTH_THRESHOLD)
return 0.0f;
return (-C - alphaTilde * lambda) / denom;
}
//!
//! \brief : Returns the delta lambda in XPBD when a constraint has four vertex degrees of freedom, applicable in 3D with damping.
//!
static PX_FORCE_INLINE __device__ float queryDeltaLambda(float C, const PxVec3& dCdx0, const PxVec3& dCdx1, const PxVec3& dCdx2,
const PxVec3& dCdx3, float alphaTilde, float lambda, float massInv0, float massInv1,
float massInv2, float massInv3, float damping, float dtInv, const float dCdT)
{
const float denom = (1.0f + damping * dtInv) * (massInv0 * dCdx0.magnitudeSquared() + massInv1 * dCdx1.magnitudeSquared() +
massInv2 * dCdx2.magnitudeSquared() + massInv3 * dCdx3.magnitudeSquared()) +
alphaTilde;
assert(denom != 0.0f);
if (denom < FEMCLOTH_THRESHOLD)
return 0.0f;
return -(C + alphaTilde * lambda + damping * dCdT) / denom;
}
/*******************************************************************************
*
*
* Deformation gradient and its derivatives
*
*
******************************************************************************/
//!
//! \brief : query deformation gradients (F \in R^{2x2})
//!
static PX_FORCE_INLINE __device__ void queryDeformationGradient_F2x2(PxVec2& f0, PxVec2& f1, const float4& QInv, const PxVec2& xp01,
const PxVec2& xp02)
{
f0 = QInv.x * xp01 + QInv.y * xp02;
f1 = QInv.z * xp01 + QInv.w * xp02;
}
//!
//! \brief : compute gradient of constraint (F \in R^{2x2})
//!
static PX_FORCE_INLINE __device__ void queryConstraintGradient_F2x2(PxVec2& grad1, PxVec2& grad2, const float4& qInv, const PxVec2& pC_pF0,
const PxVec2& pC_pF1)
{
grad1 = qInv.x * pC_pF0 + qInv.z * pC_pF1;
grad2 = qInv.y * pC_pF0 + qInv.w * pC_pF1;
}
/*******************************************************************************
*
*
* Constraint functions
*
*
******************************************************************************/
//!
//! \brief : As-Rigid-As-Possible constraint using {sqrt(||F - R||_F^2)}, F \in R^{2x2}
//!
static inline __device__ void ARAPConstraint_F2X2(float& lambda, PxVec2& dx0, PxVec2& dx1, PxVec2& dx2, float alphaTilde,
const float4& QInv, const PxVec2& x01, const PxVec2& x02, float massInv0, float massInv1,
float massInv2, const PxgFEMCloth& shFEMCloth)
{
PxVec2 f0, f1, r0, r1; // F = [f0 f1], R = [r0 r1]
PxVec2 grad0, grad1, grad2; // gradient of constraint
queryDeformationGradient_F2x2(f0, f1, QInv, x01, x02);
extractRotation2D(r0, r1, f0, f1);
PxVec2 FMinusR0 = f0 - r0;
PxVec2 FMinusR1 = f1 - r1;
const float C = sqrt(FMinusR0.dot(FMinusR0) + FMinusR1.dot(FMinusR1)); // ARAP constraint
if(C > FEMCLOTH_THRESHOLD)
{
const float CInv = 1.0f / C;
// pC/pF = [pCA_pF0 pCA_pF1]
const PxVec2 pC_pF0 = CInv * FMinusR0;
const PxVec2 pC_pF1 = CInv * FMinusR1;
queryConstraintGradient_F2x2(grad1, grad2, QInv, pC_pF0, pC_pF1);
grad0 = -grad1 - grad2;
const float deltaLambda = queryDeltaLambda(C, grad0, grad1, grad2, alphaTilde, lambda, massInv0, massInv1, massInv2);
lambda += deltaLambda;
dx0 += massInv0 * deltaLambda * grad0;
dx1 += massInv1 * deltaLambda * grad1;
dx2 += massInv2 * deltaLambda * grad2;
}
}
//!
//! \brief : Area conservation constraint
//!
static inline __device__ void areaConstraint_F2X2(float& lambda, PxVec2& dx0, PxVec2& dx1, PxVec2& dx2, float alphaTilde,
const float4& QInv, const PxVec2& x01, const PxVec2& x02, float massInv0, float massInv1,
float massInv2, float area, const PxgFEMCloth& shFEMCloth)
{
#if 1
// Area constraints
// C = |x01 X x02| / |u01 X u02| - 1.0
const PxReal x01CrossX02 = x01.x * x02.y - x01.y * x02.x;
const float undeformedAreaInv = 1.0f / area;
const float C = 0.5f * x01CrossX02 * undeformedAreaInv - 1.0f;
const PxVec2 grad1(0.5f * undeformedAreaInv * x02.y, -0.5f * undeformedAreaInv * x02.x);
const PxVec2 grad2(-0.5f * undeformedAreaInv * x01.y, 0.5f * undeformedAreaInv * x01.x);
const PxVec2 grad0 = -grad1 - grad2;
#else
// Area constraints
// C = det(F) - 1, F \in R^ { 2x2 }
PxVec2 f0, f1, r0, r1; // F = [f0 f1], R = [r0 r1]
PxVec2 grad0, grad1, grad2; // gradient of constraint
queryDeformationGradient_F2x2(f0, f1, QInv, x01, x02);
const PxReal C = f0.x * f1.y - f0.y * f1.x - 1.0f;
// pC/pF = [pCA_pF0 pCA_pF1]
const PxVec2 pC_pF0(f1.y, -f0.y);
const PxVec2 pC_pF1(-f1.x, f0.x);
queryConstraintGradient_F2x2(grad1, grad2, QInv, pC_pF0, pC_pF1);
grad0 = -grad1 - grad2;
#endif
const float deltaLambda = queryDeltaLambda(C, grad0, grad1, grad2, alphaTilde, lambda, massInv0, massInv1, massInv2);
lambda += deltaLambda;
dx0 += massInv0 * deltaLambda * grad0;
dx1 += massInv1 * deltaLambda * grad1;
dx2 += massInv2 * deltaLambda * grad2;
}
/*******************************************************************************
*
*
* Energy models
*
*
******************************************************************************/
//!
//! \brief : XPBD formulation of fixed corotated model
//!
static __device__ inline void membraneEnergySolvePerTriangle(PxgFEMCloth& shFEMCloth, float4& xx0, float4& xx1, float4& xx2, PxReal dt,
const PxsDeformableSurfaceMaterialData& material, const float4& QInv,
float vertexScale0, float vertexScale1, float vertexScale2, PxU32 lambdaIndex,
bool isShared, bool isTGS)
{
if (material.youngs < FEMCLOTH_THRESHOLD)
{
return;
}
PxVec3 x0 = PxLoad3(xx0);
PxVec3 x1 = PxLoad3(xx1);
PxVec3 x2 = PxLoad3(xx2);
const PxVec3 x01 = x1 - x0;
const PxVec3 x02 = x2 - x0;
const PxVec3 axis0 = x01.getNormalized();
PxVec3 normal = x01.cross(x02);
const PxVec3 axis1 = (normal.cross(axis0)).getNormalized();
const PxReal dt2 = dt * dt;
const PxReal det = QInv.x * QInv.w - QInv.y * QInv.z;
const PxReal area = 1.0f / (2.0f * det);
const PxReal volume = area * material.thickness;
PxVec2 dx0(0.0f), dx1(0.0f), dx2(0.0f);
float lambda0 = 0.0f, lambda1 = 0.0f;
if (!isTGS)
{
lambda0 = isShared ? shFEMCloth.mOrderedSharedTriangleLambdas[lambdaIndex].x : shFEMCloth.mOrderedNonSharedTriangleLambdas[lambdaIndex].x;
lambda1 = isShared ? shFEMCloth.mOrderedSharedTriangleLambdas[lambdaIndex].y : shFEMCloth.mOrderedNonSharedTriangleLambdas[lambdaIndex].y;
}
// Lame's parameters
const PxPair<PxReal, PxReal> lames = lameParameters(material.youngs, material.poissons);
// 1) enforcing ARAP constraint
PxVec2 xp01(axis0.dot(x01), axis1.dot(x01));
PxVec2 xp02(axis0.dot(x02), axis1.dot(x02));
// Lame's second parameters
const PxReal mu = lames.second;
const PxReal alphaTilde0 = 1.0f / (2.0f * mu * volume * dt2);
ARAPConstraint_F2X2(lambda0, dx0, dx1, dx2, alphaTilde0, QInv, xp01, xp02, vertexScale0 * xx0.w, vertexScale1 * xx1.w,
vertexScale2 * xx2.w, shFEMCloth);
// 2) enforcing area constraint
if (material.poissons > FEMCLOTH_THRESHOLD)
{
PxReal alphaTilde1 = 0.0f;
if(material.poissons < 0.5f - FEMCLOTH_THRESHOLD)
{
// Lame's first parameters
const PxReal lambda = lames.first;
alphaTilde1 = 1.0f / (lambda * volume * dt2);
}
xp01 += dx1 - dx0;
xp02 += dx2 - dx0;
areaConstraint_F2X2(lambda1, dx0, dx1, dx2, alphaTilde1, QInv, xp01, xp02, vertexScale0 * xx0.w, vertexScale1 * xx1.w,
vertexScale2 * xx2.w, area, shFEMCloth);
}
x0 += dx0.x * axis0 + dx0.y * axis1;
x1 += dx1.x * axis0 + dx1.y * axis1;
x2 += dx2.x * axis0 + dx2.y * axis1;
if (!isTGS)
{
if (isShared)
{
shFEMCloth.mOrderedSharedTriangleLambdas[lambdaIndex].x = lambda0;
shFEMCloth.mOrderedSharedTriangleLambdas[lambdaIndex].y = lambda1;
}
else
{
shFEMCloth.mOrderedNonSharedTriangleLambdas[lambdaIndex].x = lambda0;
shFEMCloth.mOrderedNonSharedTriangleLambdas[lambdaIndex].y = lambda1;
}
}
xx0.x = x0.x;
xx0.y = x0.y;
xx0.z = x0.z;
xx1.x = x1.x;
xx1.y = x1.y;
xx1.z = x1.z;
xx2.x = x2.x;
xx2.y = x2.y;
xx2.z = x2.z;
}
//!
//! \brief : XPBD formulation of "Discrete Shells"
//!
static __device__ inline void bendingEnergySolvePerTrianglePair(PxgFEMCloth& shFEMCloth, float4& x0, float4& x1, float4& x2, float4& x3,
const float4& vertexReferenceCounts, float dt, PxU32 trianglePairIndex,
bool isSharedTrianglePartition, bool isTGS)
{
const PxVec3 x02 = PxLoad3(x2 - x0);
const PxVec3 x03 = PxLoad3(x3 - x0);
const PxVec3 x13 = PxLoad3(x3 - x1);
const PxVec3 x12 = PxLoad3(x2 - x1);
const PxVec3 x23 = PxLoad3(x3 - x2);
const PxReal x23Len = x23.magnitude();
if(x23Len < FEMCLOTH_THRESHOLD)
return;
const PxReal x23LenInv = 1.f / x23Len;
const PxVec3 x23Normalized = x23 * x23LenInv;
const float4 restBendingAngle_flexuralStiffness_damping =
isSharedTrianglePartition ? shFEMCloth.mOrderedSharedRestBendingAngle_flexuralStiffness_damping[trianglePairIndex]
: shFEMCloth.mOrderedNonSharedRestBendingAngle_flexuralStiffness_damping[trianglePairIndex];
const PxReal restBendingAngle = restBendingAngle_flexuralStiffness_damping.x;
const PxReal kInv = restBendingAngle_flexuralStiffness_damping.y;
if (kInv <= 0.f)
return;
//const PxReal damping = restBendingAngle_flexuralStiffness_damping.z;
const PxVec3 scaledN0 = x02.cross(x03);
const PxVec3 scaledN1 = x13.cross(x12);
const PxReal n0LenInv = 1.f / scaledN0.magnitude();
const PxReal n1LenInv = 1.f / scaledN1.magnitude();
const PxVec3 n0 = scaledN0 * n0LenInv;
PxVec3 n1 = scaledN1 * n1LenInv;
const PxReal cosAngle = n0.dot(n1);
const PxReal sinAngle = n0.cross(n1).dot(x23Normalized);
PxReal angle = atan2f(sinAngle, cosAngle);
PxReal C = 0.f;
PxReal alphaTilde = 0.f;
float dtInv = 1.0f / dt;
alphaTilde = kInv * dtInv * dtInv;
C = angle - restBendingAngle;
if(PxAbs(C + FEMCLOTH_2PI) < PxAbs(C))
{
C += FEMCLOTH_2PI;
}
else if(PxAbs(C - FEMCLOTH_2PI) < PxAbs(C))
{
C -= FEMCLOTH_2PI;
}
// Bending constraint clamped.
C = PxClamp(C, -FEMCLOTH_HALF_PI, FEMCLOTH_HALF_PI);
const PxVec3 temp0 = n0 * n0LenInv;
const PxVec3 temp1 = n1 * n1LenInv;
const PxVec3 dCdx0 = -x23Len * temp0;
const PxVec3 dCdx1 = -x23Len * temp1;
const PxVec3 dCdx2 = x03.dot(x23Normalized) * temp0 + x13.dot(x23Normalized) * temp1;
const PxVec3 dCdx3 = -(x02.dot(x23Normalized) * temp0 + x12.dot(x23Normalized) * temp1);
PxReal lambda = 0.0f;
if (!isTGS)
{
lambda = isSharedTrianglePartition ? shFEMCloth.mSharedBendingLambdas[trianglePairIndex] :
shFEMCloth.mNonSharedBendingLambdas[trianglePairIndex];
}
float deltaLambda =
queryDeltaLambda(C, dCdx0, dCdx1, dCdx2, dCdx3, alphaTilde, lambda, vertexReferenceCounts.x * x0.w, vertexReferenceCounts.y * x1.w,
vertexReferenceCounts.z * x2.w, vertexReferenceCounts.w * x3.w);
if (!isTGS)
{
if (isSharedTrianglePartition)
{
shFEMCloth.mSharedBendingLambdas[trianglePairIndex] = lambda + deltaLambda;
}
else
{
shFEMCloth.mNonSharedBendingLambdas[trianglePairIndex] = lambda + deltaLambda;
}
}
PxReal scale0 = vertexReferenceCounts.x * x0.w * deltaLambda;
x0.x += scale0 * dCdx0.x;
x0.y += scale0 * dCdx0.y;
x0.z += scale0 * dCdx0.z;
PxReal scale1 = vertexReferenceCounts.y * x1.w * deltaLambda;
x1.x += scale1 * dCdx1.x;
x1.y += scale1 * dCdx1.y;
x1.z += scale1 * dCdx1.z;
PxReal scale2 = vertexReferenceCounts.z * x2.w * deltaLambda;
x2.x += scale2 * dCdx2.x;
x2.y += scale2 * dCdx2.y;
x2.z += scale2 * dCdx2.z;
PxReal scale3 = vertexReferenceCounts.w * x3.w * deltaLambda;
x3.x += scale3 * dCdx3.x;
x3.y += scale3 * dCdx3.y;
x3.z += scale3 * dCdx3.z;
return;
}
//!
//! \brief : Cloth shell energies in a triangle-pair (two adjacent triangles): in-plane + bending
//!
static __device__ inline
void
clothSharedEnergySolvePerTrianglePair(PxgFEMCloth& shFEMCloth, float4& x0, float4& x1, float4& x2, float4& x3,
const float4& vertexReferenceCount, const PxsDeformableSurfaceMaterialData* PX_RESTRICT clothMaterials,
float dt, PxU32 trianglePairIndex, bool isTGS)
{
// shared edge: the shared edge between two adjacent triangles (triangle0, triangle1).
// edge0, edge1: non-shared edge in triangle0 and triangle1, respectively.
// tri0Count, tri1Count: the number of references to triangle0 and triangle1 in the entire triangle pairs.
const float4 restData0 = shFEMCloth.mOrderedSharedRestEdge0_edge1[trianglePairIndex];
const float4 restData1 = shFEMCloth.mOrderedSharedRestEdgeLength_material0_material1[trianglePairIndex];
const PxU32 globalMaterialIndex0 = static_cast<PxU32>(restData1.y);
const PxU32 globalMaterialIndex1 = static_cast<PxU32>(restData1.z);
const PxVec2 restEdge0(restData0.x, restData0.y);
const PxVec2 restEdge1(restData0.z, restData0.w);
const float restSharedEdgeLength = restData1.x;
const float det0 = restSharedEdgeLength * restEdge0.y;
const float det1 = restSharedEdgeLength * restEdge1.y;
// In-plane constraint for triangle0 with vertex x2, x3, and x0.
if(PxAbs(det0) > FEMCLOTH_THRESHOLD)
{
const PxU32 lambdaIndex = 2*trianglePairIndex;
const float4 QInv0 = make_float4(restEdge0.y, 0.0f, -restEdge0.x, restSharedEdgeLength) / det0;
membraneEnergySolvePerTriangle(shFEMCloth, x2, x3, x0, dt, clothMaterials[globalMaterialIndex0], QInv0, vertexReferenceCount.z,
vertexReferenceCount.w, vertexReferenceCount.x, lambdaIndex, true, isTGS);
}
// In-plane constraint for triangle1 with vertex x2, x3, and x1.
if(PxAbs(det1) > FEMCLOTH_THRESHOLD)
{
const PxU32 lambdaIndex = 2 * trianglePairIndex + 1;
const float4 QInv1 = make_float4(restEdge1.y, 0.0f, -restEdge1.x, restSharedEdgeLength) / det1;
membraneEnergySolvePerTriangle(shFEMCloth, x2, x3, x1, dt, clothMaterials[globalMaterialIndex1], QInv1, vertexReferenceCount.z,
vertexReferenceCount.w, vertexReferenceCount.y, lambdaIndex, true, isTGS);
}
// Bending constraint for the triangle pair
bendingEnergySolvePerTrianglePair(shFEMCloth, x0, x1, x2, x3, vertexReferenceCount, dt, trianglePairIndex, true, isTGS);
}
#endif // FEMCLOTHUTIL