2019-12-26 14:45:42 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-12-10 05:02:44 -05:00
# include "ClothPhysicalMeshData.h"
# include "ClothConfigBase.h"
# include "ClothPhysicalMeshDataBase_Legacy.h"
2021-07-21 14:54:22 -04:00
# include "GPUSkinPublicDefs.h" // For MAX_TOTAL_INFLUENCES
# include "ClothTetherData.h"
2019-12-10 05:02:44 -05:00
FClothPhysicalMeshData : : FClothPhysicalMeshData ( )
: MaxBoneWeights ( 0 )
, NumFixedVerts ( 0 )
{
ClearWeightMaps ( ) ;
}
2020-04-02 14:15:35 -04:00
void FClothPhysicalMeshData : : MigrateFrom ( FClothPhysicalMeshData & ClothPhysicalMeshData )
2019-12-10 05:02:44 -05:00
{
2020-04-02 14:15:35 -04:00
if ( this ! = & ClothPhysicalMeshData )
{
Vertices = MoveTemp ( ClothPhysicalMeshData . Vertices ) ;
Normals = MoveTemp ( ClothPhysicalMeshData . Normals ) ;
2019-12-10 05:02:44 -05:00
# if WITH_EDITORONLY_DATA
2020-04-02 14:15:35 -04:00
VertexColors = MoveTemp ( ClothPhysicalMeshData . VertexColors ) ;
2019-12-10 05:02:44 -05:00
# endif
2020-04-02 14:15:35 -04:00
Indices = MoveTemp ( ClothPhysicalMeshData . Indices ) ;
WeightMaps = MoveTemp ( ClothPhysicalMeshData . WeightMaps ) ;
InverseMasses = MoveTemp ( ClothPhysicalMeshData . InverseMasses ) ;
BoneData = MoveTemp ( ClothPhysicalMeshData . BoneData ) ;
NumFixedVerts = ClothPhysicalMeshData . NumFixedVerts ;
MaxBoneWeights = ClothPhysicalMeshData . MaxBoneWeights ;
SelfCollisionIndices = MoveTemp ( ClothPhysicalMeshData . SelfCollisionIndices ) ;
2019-12-10 05:02:44 -05:00
}
}
2020-04-02 14:15:35 -04:00
void FClothPhysicalMeshData : : MigrateFrom ( UClothPhysicalMeshDataBase_Legacy * ClothPhysicalMeshDataBase )
2019-12-10 05:02:44 -05:00
{
2020-04-02 14:15:35 -04:00
Vertices = MoveTemp ( ClothPhysicalMeshDataBase - > Vertices ) ;
Normals = MoveTemp ( ClothPhysicalMeshDataBase - > Normals ) ;
2019-12-10 05:02:44 -05:00
# if WITH_EDITORONLY_DATA
2020-04-02 14:15:35 -04:00
VertexColors = MoveTemp ( ClothPhysicalMeshDataBase - > VertexColors ) ;
2019-12-10 05:02:44 -05:00
# endif
2020-04-02 14:15:35 -04:00
Indices = MoveTemp ( ClothPhysicalMeshDataBase - > Indices ) ;
InverseMasses = MoveTemp ( ClothPhysicalMeshDataBase - > InverseMasses ) ;
BoneData = MoveTemp ( ClothPhysicalMeshDataBase - > BoneData ) ;
2019-12-10 05:02:44 -05:00
NumFixedVerts = ClothPhysicalMeshDataBase - > NumFixedVerts ;
MaxBoneWeights = ClothPhysicalMeshDataBase - > MaxBoneWeights ;
2020-04-02 14:15:35 -04:00
SelfCollisionIndices = MoveTemp ( ClothPhysicalMeshDataBase - > SelfCollisionIndices ) ;
2019-12-10 05:02:44 -05:00
const TArray < uint32 > FloatArrayIds = ClothPhysicalMeshDataBase - > GetFloatArrayIds ( ) ;
for ( uint32 FloatArrayId : FloatArrayIds )
{
2020-04-02 14:15:35 -04:00
if ( TArray < float > * const FloatArray = ClothPhysicalMeshDataBase - > GetFloatArray ( FloatArrayId ) )
2019-12-10 05:02:44 -05:00
{
2020-04-02 14:15:35 -04:00
FindOrAddWeightMap ( FloatArrayId ) . Values = MoveTemp ( * FloatArray ) ;
2019-12-10 05:02:44 -05:00
}
}
}
void FClothPhysicalMeshData : : Reset ( const int32 InNumVerts , const int32 InNumIndices )
{
2022-02-02 07:59:31 -05:00
Vertices . Init ( FVector3f : : ZeroVector , InNumVerts ) ;
Normals . Init ( FVector3f : : ZeroVector , InNumVerts ) ;
2019-12-10 05:02:44 -05:00
# if WITH_EDITORONLY_DATA
VertexColors . Init ( FColor : : Black , InNumVerts ) ;
# endif //#if WITH_EDITORONLY_DATA
InverseMasses . Init ( 0.f , InNumVerts ) ;
BoneData . Reset ( InNumVerts ) ;
BoneData . AddDefaulted ( InNumVerts ) ;
Indices . Init ( 0 , InNumIndices ) ;
NumFixedVerts = 0 ;
MaxBoneWeights = 0 ;
ClearWeightMaps ( ) ;
}
void FClothPhysicalMeshData : : ClearWeightMaps ( )
{
2021-02-18 18:13:28 -04:00
// Clear all weight maps (and reserve a few slots)
2019-12-10 05:02:44 -05:00
WeightMaps . Empty ( 4 ) ;
// Add default (empty) optional maps, as these are always expected to be found
2020-08-14 06:18:22 -04:00
AddWeightMap ( EWeightMapTargetCommon : : MaxDistance ) ;
2019-12-10 05:02:44 -05:00
AddWeightMap ( EWeightMapTargetCommon : : BackstopDistance ) ;
AddWeightMap ( EWeightMapTargetCommon : : BackstopRadius ) ;
2021-02-18 18:13:28 -04:00
AddWeightMap ( EWeightMapTargetCommon : : AnimDriveStiffness ) ;
2019-12-10 05:02:44 -05:00
}
2021-02-01 15:15:23 -04:00
void FClothPhysicalMeshData : : BuildSelfCollisionData ( const TMap < FName , TObjectPtr < UClothConfigBase > > & ClothConfigs )
2019-12-10 05:02:44 -05:00
{
2021-07-21 14:54:22 -04:00
// Deprecated from 5.0 onwards
float SelfCollisionRadius = 0.f ;
2021-02-01 15:15:23 -04:00
for ( const TPair < FName , TObjectPtr < UClothConfigBase > > & ClothConfig : ClothConfigs )
2019-12-10 05:02:44 -05:00
{
2021-07-21 18:02:50 -04:00
SelfCollisionRadius = FMath : : Max ( SelfCollisionRadius , ClothConfig . Value - > GetSelfCollisionRadius ( ) ) ;
2019-12-10 05:02:44 -05:00
}
2021-07-21 14:54:22 -04:00
if ( SelfCollisionRadius )
{
BuildSelfCollisionData ( SelfCollisionRadius ) ;
}
}
2019-12-10 05:02:44 -05:00
2021-07-21 14:54:22 -04:00
void FClothPhysicalMeshData : : BuildSelfCollisionData ( float SelfCollisionRadius )
{
const float SelfCollisionRadiusSq = SelfCollisionRadius * SelfCollisionRadius ;
2019-12-10 05:02:44 -05:00
// Start with the full set
const int32 NumVerts = Vertices . Num ( ) ;
SelfCollisionIndices . Reset ( ) ;
const FPointWeightMap & MaxDistances = GetWeightMap ( EWeightMapTargetCommon : : MaxDistance ) ;
for ( int32 Index = 0 ; Index < NumVerts ; + + Index )
{
if ( ! MaxDistances . IsBelowThreshold ( Index ) )
{
SelfCollisionIndices . Add ( Index ) ;
}
}
// Now start aggressively culling verts that are near others that we have accepted
for ( int32 Vert0Itr = 0 ; Vert0Itr < SelfCollisionIndices . Num ( ) ; + + Vert0Itr )
{
const uint32 V0Index = SelfCollisionIndices [ Vert0Itr ] ;
if ( V0Index = = INDEX_NONE )
{
// We'll remove these indices later. Just skip it for now.
continue ;
}
2022-02-02 07:59:31 -05:00
const FVector & V0Pos = ( FVector ) Vertices [ V0Index ] ;
2019-12-10 05:02:44 -05:00
// Start one after our current V0, we've done the other checks
for ( int32 Vert1Itr = Vert0Itr + 1 ; Vert1Itr < SelfCollisionIndices . Num ( ) ; + + Vert1Itr )
{
const uint32 V1Index = SelfCollisionIndices [ Vert1Itr ] ;
if ( V1Index = = INDEX_NONE )
{
// We'll remove these indices later. Just skip it for now.
continue ;
}
2022-02-02 07:59:31 -05:00
const FVector & V1Pos = ( FVector ) Vertices [ V1Index ] ;
2019-12-10 05:02:44 -05:00
const float V0ToV1DistSq = ( V1Pos - V0Pos ) . SizeSquared ( ) ;
2021-07-21 14:54:22 -04:00
if ( V0ToV1DistSq < SelfCollisionRadiusSq )
2019-12-10 05:02:44 -05:00
{
// Points are in contact in the rest state. Remove it.
//
// It's worth noting that this biases towards removing indices
// of later in the list, and keeping ones earlier. That's not
// a great criteria for choosing which one is more important.
SelfCollisionIndices [ Vert1Itr ] = INDEX_NONE ;
continue ;
}
}
}
// Cull flagged indices.
for ( int32 It = SelfCollisionIndices . Num ( ) ; It - - ; )
{
if ( SelfCollisionIndices [ It ] = = INDEX_NONE )
{
SelfCollisionIndices . RemoveAt ( It ) ;
}
}
2021-07-21 14:54:22 -04:00
}
void FClothPhysicalMeshData : : CalculateInverseMasses ( )
{
// Recalculate inverse masses for the physical mesh particles
check ( Indices . Num ( ) % 3 = = 0 ) ;
const int32 NumVerts = Vertices . Num ( ) ;
InverseMasses . Empty ( NumVerts ) ;
InverseMasses . AddZeroed ( NumVerts ) ;
for ( int32 TriBaseIndex = 0 ; TriBaseIndex < Indices . Num ( ) ; TriBaseIndex + = 3 )
{
const int32 Index0 = Indices [ TriBaseIndex ] ;
const int32 Index1 = Indices [ TriBaseIndex + 1 ] ;
const int32 Index2 = Indices [ TriBaseIndex + 2 ] ;
2022-02-02 07:59:31 -05:00
const FVector AB = FVector ( Vertices [ Index1 ] - Vertices [ Index0 ] ) ;
const FVector AC = FVector ( Vertices [ Index2 ] - Vertices [ Index0 ] ) ;
2021-07-21 14:54:22 -04:00
const float TriArea = FVector : : CrossProduct ( AB , AC ) . Size ( ) ;
InverseMasses [ Index0 ] + = TriArea ;
InverseMasses [ Index1 ] + = TriArea ;
InverseMasses [ Index2 ] + = TriArea ;
}
NumFixedVerts = 0 ;
const FPointWeightMap * const MaxDistances = FindWeightMap ( EWeightMapTargetCommon : : MaxDistance ) ;
const TFunction < bool ( int32 ) > IsKinematic = ( ! MaxDistances | | ! MaxDistances - > Num ( ) ) ?
TFunction < bool ( int32 ) > ( [ ] ( int32 ) - > bool { return false ; } ) :
TFunction < bool ( int32 ) > ( [ & MaxDistances ] ( int32 Index ) - > bool { return ( * MaxDistances ) [ Index ] < SMALL_NUMBER ; } ) ; // For consistency, the default Threshold should be 0.1, not SMALL_NUMBER. But for backward compatibility it needs to be SMALL_NUMBER for now.
float MassSum = 0.0f ;
for ( int32 CurrVertIndex = 0 ; CurrVertIndex < NumVerts ; + + CurrVertIndex )
{
float & InverseMass = InverseMasses [ CurrVertIndex ] ;
if ( IsKinematic ( CurrVertIndex ) )
{
InverseMass = 0.0f ;
+ + NumFixedVerts ;
}
else
{
MassSum + = InverseMass ;
}
}
if ( MassSum > 0.0f )
{
const float MassScale = ( float ) ( NumVerts - NumFixedVerts ) / MassSum ;
for ( float & InverseMass : InverseMasses )
{
if ( InverseMass ! = 0.0f )
{
InverseMass * = MassScale ;
InverseMass = 1.0f / InverseMass ;
}
}
}
// TODO: Cache config base (Chaos) mass data
// Note that multiple configs with different mass modes will require a different
// structure than this one to store the mass.
}
void FClothPhysicalMeshData : : CalculateNumInfluences ( )
{
// Needed by all cloth implementations got skinning
for ( int32 VertIndex = 0 ; VertIndex < Vertices . Num ( ) ; + + VertIndex )
{
FClothVertBoneData & BoneDatum = BoneData [ VertIndex ] ;
const uint16 * BoneIndices = BoneDatum . BoneIndices ;
const float * BoneWeights = BoneDatum . BoneWeights ;
BoneDatum . NumInfluences = MAX_TOTAL_INFLUENCES ;
int32 NumInfluences = 0 ;
for ( int32 InfluenceIndex = 0 ; InfluenceIndex < MAX_TOTAL_INFLUENCES ; + + InfluenceIndex )
{
if ( BoneWeights [ InfluenceIndex ] = = 0.0f | | BoneIndices [ InfluenceIndex ] = = INDEX_NONE )
{
BoneDatum . NumInfluences = NumInfluences ;
break ;
}
+ + NumInfluences ;
}
}
}
void FClothPhysicalMeshData : : CalculateTethers ( bool bUseEuclideanDistance , bool bUseGeodesicDistance )
{
if ( const FPointWeightMap * const MaxDistances = FindWeightMap ( EWeightMapTargetCommon : : MaxDistance ) )
{
// Note: Technically there could be two different configs requiring different flavour of tethers,
// in reality at the time of writing there is only one type of tether involved at any one type.
if ( bUseEuclideanDistance )
{
constexpr bool bGenerateGeodesic = false ;
EuclideanTethers . GenerateTethers ( Vertices , Indices , MaxDistances - > Values , bGenerateGeodesic ) ;
}
if ( bUseGeodesicDistance )
{
constexpr bool bGenegateGeodesic = true ;
GeodesicTethers . GenerateTethers ( Vertices , Indices , MaxDistances - > Values , bGenegateGeodesic ) ;
}
}
2019-12-10 05:02:44 -05:00
}
2021-08-20 14:31:57 -04:00
2021-11-18 14:37:34 -05:00
// TODO: Deprecated, turn this into CalculateNormals() after 5.0
2021-08-20 14:31:57 -04:00
void FClothPhysicalMeshData : : ComputeFaceAveragedVertexNormals ( TArray < FVector3f > & OutNormals ) const
{
OutNormals . Init ( FVector3f { 0.f , 0.f , 0.f } , Vertices . Num ( ) ) ;
const int32 NumTris = Indices . Num ( ) / 3 ;
for ( int32 TID = 0 ; TID < NumTris ; + + TID )
{
const uint32 VA = Indices [ 3 * TID + 0 ] ;
const uint32 VB = Indices [ 3 * TID + 1 ] ;
const uint32 VC = Indices [ 3 * TID + 2 ] ;
const FVector3f & PA = Vertices [ VA ] ;
const FVector3f & PB = Vertices [ VB ] ;
const FVector3f & PC = Vertices [ VC ] ;
FVector3f Normal = FVector3f : : CrossProduct ( PC - PA , PB - PA ) ;
if ( ! Normal . Normalize ( ) )
{
// skip contributions from degenerate triangles
continue ;
}
OutNormals [ VA ] + = Normal ;
OutNormals [ VB ] + = Normal ;
OutNormals [ VC ] + = Normal ;
}
for ( int32 VertexID = 0 ; VertexID < OutNormals . Num ( ) ; + + VertexID )
{
FVector3f & N = OutNormals [ VertexID ] ;
if ( ! N . Normalize ( ) )
{
N = FVector3f : : XAxisVector ;
}
}
}