You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
[FYI] brian.karis, chris.morrison, bryce.lumpkin [CL 22459671 by jian ru in ue5-main branch]
675 lines
19 KiB
C++
675 lines
19 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Cluster.h"
|
|
#include "GraphPartitioner.h"
|
|
|
|
namespace Nanite
|
|
{
|
|
|
|
void CorrectAttributes( float* Attributes )
|
|
{
|
|
FVector3f& Normal = *reinterpret_cast< FVector3f* >( Attributes );
|
|
Normal.Normalize();
|
|
}
|
|
|
|
void CorrectAttributesColor( float* Attributes )
|
|
{
|
|
CorrectAttributes( Attributes );
|
|
|
|
FLinearColor& Color = *reinterpret_cast< FLinearColor* >( Attributes + 3 );
|
|
Color = Color.GetClamped();
|
|
}
|
|
|
|
|
|
FCluster::FCluster(
|
|
const TArray< FStaticMeshBuildVertex >& InVerts,
|
|
const TArrayView< const uint32 >& InIndexes,
|
|
const TArrayView< const int32 >& InMaterialIndexes,
|
|
uint32 InNumTexCoords, bool bInHasColors, bool bInPreserveArea,
|
|
uint32 TriBegin, uint32 TriEnd, const FGraphPartitioner& Partitioner, const FAdjacency& Adjacency )
|
|
{
|
|
GUID = (uint64(TriBegin) << 32) | TriEnd;
|
|
|
|
NumTris = TriEnd - TriBegin;
|
|
//ensure(NumTriangles <= FCluster::ClusterSize);
|
|
|
|
bHasColors = bInHasColors;
|
|
bPreserveArea = bInPreserveArea;
|
|
NumTexCoords = InNumTexCoords;
|
|
|
|
Verts.Reserve( NumTris * GetVertSize() );
|
|
Indexes.Reserve( 3 * NumTris );
|
|
MaterialIndexes.Reserve( NumTris );
|
|
ExternalEdges.Reserve( 3 * NumTris );
|
|
NumExternalEdges = 0;
|
|
|
|
check(InMaterialIndexes.Num() * 3 == InIndexes.Num());
|
|
|
|
TMap< uint32, uint32 > OldToNewIndex;
|
|
OldToNewIndex.Reserve( NumTris );
|
|
|
|
for( uint32 i = TriBegin; i < TriEnd; i++ )
|
|
{
|
|
uint32 TriIndex = Partitioner.Indexes[i];
|
|
|
|
for( uint32 k = 0; k < 3; k++ )
|
|
{
|
|
uint32 OldIndex = InIndexes[ TriIndex * 3 + k ];
|
|
uint32* NewIndexPtr = OldToNewIndex.Find( OldIndex );
|
|
uint32 NewIndex = NewIndexPtr ? *NewIndexPtr : ~0u;
|
|
|
|
if( NewIndex == ~0u )
|
|
{
|
|
Verts.AddUninitialized( GetVertSize() );
|
|
NewIndex = NumVerts++;
|
|
OldToNewIndex.Add( OldIndex, NewIndex );
|
|
|
|
const FStaticMeshBuildVertex& InVert = InVerts[ OldIndex ];
|
|
|
|
GetPosition( NewIndex ) = InVert.Position;
|
|
GetNormal( NewIndex ) = InVert.TangentZ;
|
|
|
|
if( bHasColors )
|
|
{
|
|
GetColor( NewIndex ) = InVert.Color.ReinterpretAsLinear();
|
|
}
|
|
|
|
FVector2f* UVs = GetUVs( NewIndex );
|
|
for( uint32 UVIndex = 0; UVIndex < NumTexCoords; UVIndex++ )
|
|
{
|
|
UVs[ UVIndex ] = InVert.UVs[ UVIndex ];
|
|
}
|
|
}
|
|
|
|
Indexes.Add( NewIndex );
|
|
|
|
int32 EdgeIndex = TriIndex * 3 + k;
|
|
int32 AdjCount = 0;
|
|
|
|
Adjacency.ForAll( EdgeIndex,
|
|
[ &AdjCount, TriBegin, TriEnd, &Partitioner ]( int32 EdgeIndex, int32 AdjIndex )
|
|
{
|
|
uint32 AdjTri = Partitioner.SortedTo[ AdjIndex / 3 ];
|
|
if( AdjTri < TriBegin || AdjTri >= TriEnd )
|
|
AdjCount++;
|
|
} );
|
|
|
|
ExternalEdges.Add( AdjCount );
|
|
NumExternalEdges += AdjCount != 0 ? 1 : 0;
|
|
}
|
|
|
|
MaterialIndexes.Add( InMaterialIndexes[ TriIndex ] );
|
|
}
|
|
|
|
SanitizeVertexData();
|
|
|
|
for( uint32 VertexIndex = 0; VertexIndex < NumVerts; VertexIndex++ )
|
|
{
|
|
float* Attributes = GetAttributes( VertexIndex );
|
|
|
|
// Make sure this vertex is valid from the start
|
|
if( bHasColors )
|
|
CorrectAttributesColor( Attributes );
|
|
else
|
|
CorrectAttributes( Attributes );
|
|
}
|
|
|
|
Bound();
|
|
}
|
|
|
|
// Split
|
|
FCluster::FCluster( FCluster& SrcCluster, uint32 TriBegin, uint32 TriEnd, const FGraphPartitioner& Partitioner, const FAdjacency& Adjacency )
|
|
: MipLevel( SrcCluster.MipLevel )
|
|
{
|
|
GUID = MurmurFinalize64(SrcCluster.GUID) ^ ((uint64(TriBegin) << 32) | TriEnd);
|
|
|
|
NumTexCoords = SrcCluster.NumTexCoords;
|
|
bHasColors = SrcCluster.bHasColors;
|
|
bPreserveArea = SrcCluster.bPreserveArea;
|
|
|
|
NumTris = TriEnd - TriBegin;
|
|
|
|
Verts.Reserve( NumTris * GetVertSize() );
|
|
Indexes.Reserve( 3 * NumTris );
|
|
MaterialIndexes.Reserve( NumTris );
|
|
ExternalEdges.Reserve( 3 * NumTris );
|
|
NumExternalEdges = 0;
|
|
|
|
TMap< uint32, uint32 > OldToNewIndex;
|
|
OldToNewIndex.Reserve( NumTris );
|
|
|
|
for( uint32 i = TriBegin; i < TriEnd; i++ )
|
|
{
|
|
uint32 TriIndex = Partitioner.Indexes[i];
|
|
|
|
for( uint32 k = 0; k < 3; k++ )
|
|
{
|
|
uint32 OldIndex = SrcCluster.Indexes[ TriIndex * 3 + k ];
|
|
uint32* NewIndexPtr = OldToNewIndex.Find( OldIndex );
|
|
uint32 NewIndex = NewIndexPtr ? *NewIndexPtr : ~0u;
|
|
|
|
if( NewIndex == ~0u )
|
|
{
|
|
Verts.AddUninitialized( GetVertSize() );
|
|
NewIndex = NumVerts++;
|
|
OldToNewIndex.Add( OldIndex, NewIndex );
|
|
|
|
FMemory::Memcpy( &GetPosition( NewIndex ), &SrcCluster.GetPosition( OldIndex ), GetVertSize() * sizeof( float ) );
|
|
}
|
|
|
|
Indexes.Add( NewIndex );
|
|
|
|
int32 EdgeIndex = TriIndex * 3 + k;
|
|
int32 AdjCount = SrcCluster.ExternalEdges[ EdgeIndex ];
|
|
|
|
Adjacency.ForAll( EdgeIndex,
|
|
[ &AdjCount, TriBegin, TriEnd, &Partitioner ]( int32 EdgeIndex, int32 AdjIndex )
|
|
{
|
|
uint32 AdjTri = Partitioner.SortedTo[ AdjIndex / 3 ];
|
|
if( AdjTri < TriBegin || AdjTri >= TriEnd )
|
|
AdjCount++;
|
|
} );
|
|
|
|
ExternalEdges.Add( AdjCount );
|
|
NumExternalEdges += AdjCount != 0 ? 1 : 0;
|
|
}
|
|
|
|
MaterialIndexes.Add( SrcCluster.MaterialIndexes[ TriIndex ] );
|
|
}
|
|
|
|
Bound();
|
|
}
|
|
|
|
// Merge
|
|
FCluster::FCluster( const TArray< const FCluster*, TInlineAllocator<32> >& MergeList )
|
|
{
|
|
NumTexCoords = MergeList[0]->NumTexCoords;
|
|
bHasColors = MergeList[0]->bHasColors;
|
|
bPreserveArea = MergeList[0]->bPreserveArea;
|
|
|
|
const uint32 NumTrisGuess = ClusterSize * MergeList.Num();
|
|
|
|
Verts.Reserve( NumTrisGuess * GetVertSize() );
|
|
Indexes.Reserve( 3 * NumTrisGuess );
|
|
MaterialIndexes.Reserve( NumTrisGuess );
|
|
ExternalEdges.Reserve( 3 * NumTrisGuess );
|
|
NumExternalEdges = 0;
|
|
|
|
FHashTable VertHashTable( 1 << FMath::FloorLog2( NumTrisGuess ), NumTrisGuess );
|
|
|
|
for( const FCluster* Child : MergeList )
|
|
{
|
|
NumTris += Child->NumTris;
|
|
Bounds += Child->Bounds;
|
|
SurfaceArea += Child->SurfaceArea;
|
|
|
|
// Can jump multiple levels but guarantee it steps at least 1.
|
|
MipLevel = FMath::Max( MipLevel, Child->MipLevel + 1 );
|
|
LODError = FMath::Max( LODError, Child->LODError );
|
|
EdgeLength = FMath::Max( EdgeLength, Child->EdgeLength );
|
|
|
|
for( int32 i = 0; i < Child->Indexes.Num(); i++ )
|
|
{
|
|
uint32 NewIndex = AddVert( &Child->Verts[ Child->Indexes[i] * GetVertSize() ], VertHashTable );
|
|
|
|
Indexes.Add( NewIndex );
|
|
}
|
|
|
|
ExternalEdges.Append( Child->ExternalEdges );
|
|
MaterialIndexes.Append( Child->MaterialIndexes );
|
|
|
|
GUID = MurmurFinalize64(GUID) ^ Child->GUID;
|
|
}
|
|
|
|
FAdjacency Adjacency = BuildAdjacency();
|
|
|
|
int32 ChildIndex = 0;
|
|
int32 MinIndex = 0;
|
|
int32 MaxIndex = MergeList[0]->ExternalEdges.Num();
|
|
|
|
for( int32 EdgeIndex = 0; EdgeIndex < ExternalEdges.Num(); EdgeIndex++ )
|
|
{
|
|
if( EdgeIndex >= MaxIndex )
|
|
{
|
|
ChildIndex++;
|
|
MinIndex = MaxIndex;
|
|
MaxIndex += MergeList[ ChildIndex ]->ExternalEdges.Num();
|
|
}
|
|
|
|
int32 AdjCount = ExternalEdges[ EdgeIndex ];
|
|
|
|
Adjacency.ForAll( EdgeIndex,
|
|
[ &AdjCount, MinIndex, MaxIndex ]( int32 EdgeIndex, int32 AdjIndex )
|
|
{
|
|
if( AdjIndex < MinIndex || AdjIndex >= MaxIndex )
|
|
AdjCount--;
|
|
} );
|
|
|
|
// This seems like a sloppy workaround for a bug elsewhere but it is possible an interior edge is moved during simplifiation to
|
|
// match another cluster and it isn't reflected in this count. Sounds unlikely but any hole closing could do this.
|
|
// The only way to catch it would be to rebuild full adjacency after every pass which isn't practical.
|
|
AdjCount = FMath::Max( AdjCount, 0 );
|
|
|
|
ExternalEdges[ EdgeIndex ] = AdjCount;
|
|
NumExternalEdges += AdjCount != 0 ? 1 : 0;
|
|
}
|
|
|
|
ensure( NumTris == Indexes.Num() / 3 );
|
|
}
|
|
|
|
float FCluster::Simplify( uint32 TargetNumTris, float TargetError, uint32 LimitNumTris, bool bForNaniteFallback )
|
|
{
|
|
if( ( TargetNumTris >= NumTris && TargetError == 0.0f ) || LimitNumTris >= NumTris )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
float UVArea[ MAX_STATIC_TEXCOORDS ] = { 0.0f };
|
|
|
|
for( uint32 TriIndex = 0; TriIndex < NumTris; TriIndex++ )
|
|
{
|
|
uint32 Index0 = Indexes[ TriIndex * 3 + 0 ];
|
|
uint32 Index1 = Indexes[ TriIndex * 3 + 1 ];
|
|
uint32 Index2 = Indexes[ TriIndex * 3 + 2 ];
|
|
|
|
FVector2f* UV0 = GetUVs( Index0 );
|
|
FVector2f* UV1 = GetUVs( Index1 );
|
|
FVector2f* UV2 = GetUVs( Index2 );
|
|
|
|
for( uint32 UVIndex = 0; UVIndex < NumTexCoords; UVIndex++ )
|
|
{
|
|
FVector2f EdgeUV1 = UV1[ UVIndex ] - UV0[ UVIndex ];
|
|
FVector2f EdgeUV2 = UV2[ UVIndex ] - UV0[ UVIndex ];
|
|
float SignedArea = 0.5f * ( EdgeUV1 ^ EdgeUV2 );
|
|
UVArea[ UVIndex ] += FMath::Abs( SignedArea );
|
|
|
|
// Force an attribute discontinuity for UV mirroring edges.
|
|
// Quadric could account for this but requires much larger UV weights which raises error on meshes which have no visible issues otherwise.
|
|
MaterialIndexes[ TriIndex ] |= ( SignedArea >= 0.0f ? 1 : 0 ) << ( UVIndex + 24 );
|
|
}
|
|
}
|
|
|
|
float TriangleSize = FMath::Sqrt( SurfaceArea / NumTris );
|
|
|
|
FFloat32 CurrentSize( FMath::Max( TriangleSize, THRESH_POINTS_ARE_SAME ) );
|
|
FFloat32 DesiredSize( 0.25f );
|
|
FFloat32 FloatScale( 1.0f );
|
|
|
|
// Lossless scaling by only changing the float exponent.
|
|
int32 Exponent = FMath::Clamp( (int)DesiredSize.Components.Exponent - (int)CurrentSize.Components.Exponent, -126, 127 );
|
|
FloatScale.Components.Exponent = Exponent + 127; //ExpBias
|
|
// Scale ~= DesiredSize / CurrentSize
|
|
// When generating nanite fallback meshes, use the same weights as in FQuadricSimplifierMeshReduction::ReduceMeshDescription
|
|
// to ensure consistent LOD transition screen size.
|
|
float PositionScale = bForNaniteFallback ? 1.f : FloatScale.FloatValue;
|
|
|
|
for( uint32 i = 0; i < NumVerts; i++ )
|
|
{
|
|
GetPosition(i) *= PositionScale;
|
|
}
|
|
TargetError *= PositionScale;
|
|
|
|
uint32 NumAttributes = GetVertSize() - 3;
|
|
float* AttributeWeights = (float*)FMemory_Alloca( NumAttributes * sizeof( float ) );
|
|
|
|
// Normal
|
|
AttributeWeights[0] = bForNaniteFallback ? 16.f : 1.0f;
|
|
AttributeWeights[1] = bForNaniteFallback ? 16.f : 1.0f;
|
|
AttributeWeights[2] = bForNaniteFallback ? 16.f : 1.0f;
|
|
|
|
if( bHasColors )
|
|
{
|
|
float* ColorWeights = AttributeWeights + 3;
|
|
ColorWeights[0] = bForNaniteFallback ? 0.1f : 0.0625f;
|
|
ColorWeights[1] = bForNaniteFallback ? 0.1f : 0.0625f;
|
|
ColorWeights[2] = bForNaniteFallback ? 0.1f : 0.0625f;
|
|
ColorWeights[3] = bForNaniteFallback ? 0.1f : 0.0625f;
|
|
}
|
|
|
|
uint32 TexCoordOffset = 3 + ( bHasColors ? 4 : 0 );
|
|
float* UVWeights = AttributeWeights + TexCoordOffset;
|
|
|
|
// Normalize UVWeights
|
|
for( uint32 UVIndex = 0; UVIndex < NumTexCoords; UVIndex++ )
|
|
{
|
|
if (bForNaniteFallback)
|
|
{
|
|
float MinUV = +FLT_MAX;
|
|
float MaxUV = -FLT_MAX;
|
|
|
|
for (uint32 TriIndex = 0; TriIndex < NumTris; TriIndex++)
|
|
{
|
|
uint32 Index0 = Indexes[TriIndex * 3 + 0];
|
|
uint32 Index1 = Indexes[TriIndex * 3 + 1];
|
|
uint32 Index2 = Indexes[TriIndex * 3 + 2];
|
|
|
|
FVector2f UV0 = GetUVs(Index0)[UVIndex];
|
|
FVector2f UV1 = GetUVs(Index1)[UVIndex];
|
|
FVector2f UV2 = GetUVs(Index2)[UVIndex];
|
|
|
|
MinUV = FMath::Min(MinUV, UV0.X);
|
|
MinUV = FMath::Min(MinUV, UV0.Y);
|
|
MinUV = FMath::Min(MinUV, UV1.X);
|
|
MinUV = FMath::Min(MinUV, UV1.Y);
|
|
MinUV = FMath::Min(MinUV, UV2.X);
|
|
MinUV = FMath::Min(MinUV, UV2.Y);
|
|
|
|
MaxUV = FMath::Max(MaxUV, UV0.X);
|
|
MaxUV = FMath::Max(MaxUV, UV0.Y);
|
|
MaxUV = FMath::Max(MaxUV, UV1.X);
|
|
MaxUV = FMath::Max(MaxUV, UV1.Y);
|
|
MaxUV = FMath::Max(MaxUV, UV2.X);
|
|
MaxUV = FMath::Max(MaxUV, UV2.Y);
|
|
}
|
|
|
|
UVWeights[2 * UVIndex + 0] = 0.5f / FMath::Max(1.f, MaxUV - MinUV);
|
|
UVWeights[2 * UVIndex + 1] = 0.5f / FMath::Max(1.f, MaxUV - MinUV);
|
|
}
|
|
else
|
|
{
|
|
float TriangleUVSize = FMath::Sqrt(UVArea[UVIndex] / NumTris);
|
|
TriangleUVSize = FMath::Max(TriangleUVSize, THRESH_UVS_ARE_SAME);
|
|
|
|
UVWeights[2 * UVIndex + 0] = 1.0f / (128.0f * TriangleUVSize);
|
|
UVWeights[2 * UVIndex + 1] = 1.0f / (128.0f * TriangleUVSize);
|
|
}
|
|
}
|
|
|
|
FMeshSimplifier Simplifier( Verts.GetData(), NumVerts, Indexes.GetData(), Indexes.Num(), MaterialIndexes.GetData(), NumAttributes );
|
|
|
|
TMap< TTuple< FVector3f, FVector3f >, int8 > LockedEdges;
|
|
|
|
for( int32 EdgeIndex = 0; EdgeIndex < ExternalEdges.Num(); EdgeIndex++ )
|
|
{
|
|
if( ExternalEdges[ EdgeIndex ] )
|
|
{
|
|
uint32 VertIndex0 = Indexes[ EdgeIndex ];
|
|
uint32 VertIndex1 = Indexes[ Cycle3( EdgeIndex ) ];
|
|
|
|
const FVector3f& Position0 = GetPosition( VertIndex0 );
|
|
const FVector3f& Position1 = GetPosition( VertIndex1 );
|
|
|
|
Simplifier.LockPosition( Position0 );
|
|
Simplifier.LockPosition( Position1 );
|
|
|
|
LockedEdges.Add( MakeTuple( Position0, Position1 ), ExternalEdges[ EdgeIndex ] );
|
|
}
|
|
}
|
|
|
|
Simplifier.SetAttributeWeights( AttributeWeights );
|
|
Simplifier.SetCorrectAttributes( bHasColors ? CorrectAttributesColor : CorrectAttributes );
|
|
Simplifier.SetEdgeWeight(bForNaniteFallback ? 512.f : 2.0f );
|
|
|
|
if (bForNaniteFallback)
|
|
{
|
|
Simplifier.SetLimitErrorToSurfaceArea(false);
|
|
Simplifier.DegreePenalty = 100.0f;
|
|
Simplifier.InversionPenalty = 1000000.0f;
|
|
}
|
|
|
|
float MaxErrorSqr = Simplifier.Simplify(
|
|
NumVerts, TargetNumTris, FMath::Square( TargetError ),
|
|
0, LimitNumTris, MAX_flt );
|
|
|
|
check( Simplifier.GetRemainingNumVerts() > 0 );
|
|
check( Simplifier.GetRemainingNumTris() > 0 );
|
|
|
|
if( bPreserveArea )
|
|
Simplifier.PreserveSurfaceArea();
|
|
|
|
Simplifier.Compact();
|
|
|
|
Verts.SetNum( Simplifier.GetRemainingNumVerts() * GetVertSize() );
|
|
Indexes.SetNum( Simplifier.GetRemainingNumTris() * 3 );
|
|
MaterialIndexes.SetNum( Simplifier.GetRemainingNumTris() );
|
|
ExternalEdges.Init( 0, Simplifier.GetRemainingNumTris() * 3 );
|
|
|
|
NumVerts = Simplifier.GetRemainingNumVerts();
|
|
NumTris = Simplifier.GetRemainingNumTris();
|
|
|
|
NumExternalEdges = 0;
|
|
for( int32 EdgeIndex = 0; EdgeIndex < ExternalEdges.Num(); EdgeIndex++ )
|
|
{
|
|
auto Edge = MakeTuple(
|
|
GetPosition( Indexes[ EdgeIndex ] ),
|
|
GetPosition( Indexes[ Cycle3( EdgeIndex ) ] )
|
|
);
|
|
int8* AdjCount = LockedEdges.Find( Edge );
|
|
if( AdjCount )
|
|
{
|
|
ExternalEdges[ EdgeIndex ] = *AdjCount;
|
|
NumExternalEdges++;
|
|
}
|
|
}
|
|
|
|
float InvScale = 1.0f / PositionScale;
|
|
for( uint32 i = 0; i < NumVerts; i++ )
|
|
{
|
|
GetPosition(i) *= InvScale;
|
|
Bounds += GetPosition(i);
|
|
}
|
|
|
|
for( uint32 TriIndex = 0; TriIndex < NumTris; TriIndex++ )
|
|
{
|
|
// Remove UV mirroring bits
|
|
MaterialIndexes[ TriIndex ] &= 0xffffff;
|
|
}
|
|
|
|
return FMath::Sqrt( MaxErrorSqr ) * InvScale;
|
|
}
|
|
|
|
void FCluster::Split( FGraphPartitioner& Partitioner, const FAdjacency& Adjacency ) const
|
|
{
|
|
FDisjointSet DisjointSet( NumTris );
|
|
for( int32 EdgeIndex = 0; EdgeIndex < Indexes.Num(); EdgeIndex++ )
|
|
{
|
|
Adjacency.ForAll( EdgeIndex,
|
|
[ &DisjointSet ]( int32 EdgeIndex0, int32 EdgeIndex1 )
|
|
{
|
|
if( EdgeIndex0 > EdgeIndex1 )
|
|
DisjointSet.UnionSequential( EdgeIndex0 / 3, EdgeIndex1 / 3 );
|
|
} );
|
|
}
|
|
|
|
auto GetCenter = [ this ]( uint32 TriIndex )
|
|
{
|
|
FVector3f Center;
|
|
Center = GetPosition( Indexes[ TriIndex * 3 + 0 ] );
|
|
Center += GetPosition( Indexes[ TriIndex * 3 + 1 ] );
|
|
Center += GetPosition( Indexes[ TriIndex * 3 + 2 ] );
|
|
return Center * (1.0f / 3.0f);
|
|
};
|
|
|
|
Partitioner.BuildLocalityLinks( DisjointSet, Bounds, MaterialIndexes, GetCenter );
|
|
|
|
auto* RESTRICT Graph = Partitioner.NewGraph( NumTris * 3 );
|
|
|
|
for( uint32 i = 0; i < NumTris; i++ )
|
|
{
|
|
Graph->AdjacencyOffset[i] = Graph->Adjacency.Num();
|
|
|
|
uint32 TriIndex = Partitioner.Indexes[i];
|
|
|
|
// Add shared edges
|
|
for( int k = 0; k < 3; k++ )
|
|
{
|
|
Adjacency.ForAll( 3 * TriIndex + k,
|
|
[ &Partitioner, Graph ]( int32 EdgeIndex, int32 AdjIndex )
|
|
{
|
|
Partitioner.AddAdjacency( Graph, AdjIndex / 3, 4 * 65 );
|
|
} );
|
|
}
|
|
|
|
Partitioner.AddLocalityLinks( Graph, TriIndex, 1 );
|
|
}
|
|
Graph->AdjacencyOffset[ NumTris ] = Graph->Adjacency.Num();
|
|
|
|
Partitioner.PartitionStrict( Graph, ClusterSize - 4, ClusterSize, false );
|
|
}
|
|
|
|
FAdjacency FCluster::BuildAdjacency() const
|
|
{
|
|
FAdjacency Adjacency( Indexes.Num() );
|
|
FEdgeHash EdgeHash( Indexes.Num() );
|
|
|
|
for( int32 EdgeIndex = 0; EdgeIndex < Indexes.Num(); EdgeIndex++ )
|
|
{
|
|
Adjacency.Direct[ EdgeIndex ] = -1;
|
|
|
|
EdgeHash.ForAllMatching( EdgeIndex, true,
|
|
[ this ]( int32 CornerIndex )
|
|
{
|
|
return GetPosition( Indexes[ CornerIndex ] );
|
|
},
|
|
[&]( int32 EdgeIndex, int32 OtherEdgeIndex )
|
|
{
|
|
Adjacency.Link( EdgeIndex, OtherEdgeIndex );
|
|
} );
|
|
}
|
|
|
|
return Adjacency;
|
|
}
|
|
|
|
uint32 FCluster::AddVert( const float* Vert, FHashTable& HashTable )
|
|
{
|
|
const uint32 VertSize = GetVertSize();
|
|
const FVector3f& Position = *reinterpret_cast< const FVector3f* >( Vert );
|
|
|
|
uint32 Hash = HashPosition( Position );
|
|
uint32 NewIndex;
|
|
for( NewIndex = HashTable.First( Hash ); HashTable.IsValid( NewIndex ); NewIndex = HashTable.Next( NewIndex ) )
|
|
{
|
|
uint32 i;
|
|
for( i = 0; i < VertSize; i++ )
|
|
{
|
|
if( Vert[i] != Verts[ NewIndex * VertSize + i ] )
|
|
break;
|
|
}
|
|
if( i == VertSize )
|
|
break;
|
|
}
|
|
if( !HashTable.IsValid( NewIndex ) )
|
|
{
|
|
Verts.AddUninitialized( VertSize );
|
|
NewIndex = NumVerts++;
|
|
HashTable.Add( Hash, NewIndex );
|
|
|
|
FMemory::Memcpy( &GetPosition( NewIndex ), Vert, GetVertSize() * sizeof( float ) );
|
|
}
|
|
|
|
return NewIndex;
|
|
}
|
|
|
|
void FCluster::Bound()
|
|
{
|
|
Bounds = FBounds3f();
|
|
SurfaceArea = 0.0f;
|
|
|
|
TArray< FVector3f, TInlineAllocator<128> > Positions;
|
|
Positions.SetNum( NumVerts, false );
|
|
|
|
for( uint32 i = 0; i < NumVerts; i++ )
|
|
{
|
|
Positions[i] = GetPosition(i);
|
|
Bounds += Positions[i];
|
|
}
|
|
SphereBounds = FSphere3f( Positions.GetData(), Positions.Num() );
|
|
LODBounds = SphereBounds;
|
|
|
|
float MaxEdgeLength2 = 0.0f;
|
|
for( int i = 0; i < Indexes.Num(); i += 3 )
|
|
{
|
|
FVector3f v[3];
|
|
v[0] = GetPosition( Indexes[ i + 0 ] );
|
|
v[1] = GetPosition( Indexes[ i + 1 ] );
|
|
v[2] = GetPosition( Indexes[ i + 2 ] );
|
|
|
|
FVector3f Edge01 = v[1] - v[0];
|
|
FVector3f Edge12 = v[2] - v[1];
|
|
FVector3f Edge20 = v[0] - v[2];
|
|
|
|
MaxEdgeLength2 = FMath::Max( MaxEdgeLength2, Edge01.SizeSquared() );
|
|
MaxEdgeLength2 = FMath::Max( MaxEdgeLength2, Edge12.SizeSquared() );
|
|
MaxEdgeLength2 = FMath::Max( MaxEdgeLength2, Edge20.SizeSquared() );
|
|
|
|
float TriArea = 0.5f * ( Edge01 ^ Edge20 ).Size();
|
|
SurfaceArea += TriArea;
|
|
}
|
|
EdgeLength = FMath::Sqrt( MaxEdgeLength2 );
|
|
}
|
|
|
|
static void SanitizeFloat( float& X, float MinValue, float MaxValue, float DefaultValue )
|
|
{
|
|
if( X >= MinValue && X <= MaxValue )
|
|
;
|
|
else if( X < MinValue )
|
|
X = MinValue;
|
|
else if( X > MaxValue )
|
|
X = MaxValue;
|
|
else
|
|
X = DefaultValue;
|
|
}
|
|
|
|
void FCluster::SanitizeVertexData()
|
|
{
|
|
const float FltThreshold = 1e12f; // Fairly arbitrary threshold for sensible float values.
|
|
// Should be large enough for all practical purposes, while still leaving enough headroom
|
|
// so that overflows shouldn't be a concern.
|
|
// With a 1e12 threshold, even x^3 fits comfortable in float range.
|
|
|
|
for( uint32 VertexIndex = 0; VertexIndex < NumVerts; VertexIndex++ )
|
|
{
|
|
FVector3f& Position = GetPosition( VertexIndex );
|
|
SanitizeFloat( Position.X, -FltThreshold, FltThreshold, 0.0f );
|
|
SanitizeFloat( Position.Y, -FltThreshold, FltThreshold, 0.0f );
|
|
SanitizeFloat( Position.Z, -FltThreshold, FltThreshold, 0.0f );
|
|
|
|
FVector3f& Normal = GetNormal( VertexIndex );
|
|
if( !( Normal.X >= -FltThreshold && Normal.X <= FltThreshold &&
|
|
Normal.Y >= -FltThreshold && Normal.Y <= FltThreshold &&
|
|
Normal.Z >= -FltThreshold && Normal.Z <= FltThreshold ) ) // Don't flip condition. Intentionally written like this to be NaN-safe
|
|
{
|
|
Normal = FVector3f::UpVector;
|
|
}
|
|
|
|
if( bHasColors )
|
|
{
|
|
FLinearColor& Color = GetColor( VertexIndex );
|
|
SanitizeFloat( Color.R, 0.0f, 1.0f, 1.0f );
|
|
SanitizeFloat( Color.G, 0.0f, 1.0f, 1.0f );
|
|
SanitizeFloat( Color.B, 0.0f, 1.0f, 1.0f );
|
|
SanitizeFloat( Color.A, 0.0f, 1.0f, 1.0f );
|
|
}
|
|
|
|
FVector2f* UVs = GetUVs( VertexIndex );
|
|
for( uint32 UvIndex = 0; UvIndex < NumTexCoords; UvIndex++ )
|
|
{
|
|
SanitizeFloat( UVs[ UvIndex ].X, -FltThreshold, FltThreshold, 0.0f );
|
|
SanitizeFloat( UVs[ UvIndex ].Y, -FltThreshold, FltThreshold, 0.0f );
|
|
}
|
|
}
|
|
}
|
|
|
|
FArchive& operator<<(FArchive& Ar, FMaterialRange& Range)
|
|
{
|
|
Ar << Range.RangeStart;
|
|
Ar << Range.RangeLength;
|
|
Ar << Range.MaterialIndex;
|
|
Ar << Range.BatchTriCounts;
|
|
return Ar;
|
|
}
|
|
|
|
FArchive& operator<<(FArchive& Ar, FStripDesc& Desc)
|
|
{
|
|
for (uint32 i = 0; i < 4; i++)
|
|
{
|
|
for (uint32 j = 0; j < 3; j++)
|
|
{
|
|
Ar << Desc.Bitmasks[i][j];
|
|
}
|
|
}
|
|
Ar << Desc.NumPrevRefVerticesBeforeDwords;
|
|
Ar << Desc.NumPrevNewVerticesBeforeDwords;
|
|
return Ar;
|
|
}
|
|
} // namespace Nanite
|