Files
UnrealEngine/Engine/Source/Developer/MeshSimplifier/Private/MeshSimplify.h
2025-05-18 13:04:45 +08:00

242 lines
6.9 KiB
C++

// Copyright (C) 2009 Nine Realms, Inc
//
#pragma once
#include "CoreMinimal.h"
#define SIMP_REBASE 1
#include "Quadric.h"
#include "Containers/HashTable.h"
#include "Containers/BinaryHeap.h"
#include "DisjointSet.h"
FORCEINLINE uint32 HashPosition( const FVector3f& Position )
{
union { float f; uint32 i; } x;
union { float f; uint32 i; } y;
union { float f; uint32 i; } z;
x.f = Position.X;
y.f = Position.Y;
z.f = Position.Z;
return Murmur32( {
Position.X == 0.0f ? 0u : x.i,
Position.Y == 0.0f ? 0u : y.i,
Position.Z == 0.0f ? 0u : z.i
} );
}
FORCEINLINE uint32 Cycle3( uint32 Value )
{
uint32 ValueMod3 = Value % 3;
uint32 Value1Mod3 = ( 1 << ValueMod3 ) & 3;
return Value - ValueMod3 + Value1Mod3;
}
FORCEINLINE uint32 Cycle3( uint32 Value, uint32 Offset )
{
return Value - Value % 3 + ( Value + Offset ) % 3;
}
class FMeshSimplifier
{
public:
QUADRICMESHREDUCTION_API FMeshSimplifier( float* Verts, uint32 NumVerts, uint32* Indexes, uint32 NumIndexes, int32* MaterialIndexes, uint32 NumAttributes );
~FMeshSimplifier() = default;
void SetAttributeWeights( const float* Weights ) { AttributeWeights = Weights; }
void SetEdgeWeight( float Weight ) { EdgeWeight = Weight; }
void SetMaxEdgeLengthFactor( float Factor ) { MaxEdgeLengthFactor = Factor; }
void SetCorrectAttributes( void (*Function)( float* ) ) { CorrectAttributes = Function; }
void SetLimitErrorToSurfaceArea( bool Value ) { bLimitErrorToSurfaceArea = Value; }
QUADRICMESHREDUCTION_API void LockPosition( const FVector3f& Position );
QUADRICMESHREDUCTION_API float Simplify(
uint32 TargetNumVerts, uint32 TargetNumTris, float TargetError,
uint32 LimitNumVerts, uint32 LimitNumTris, float LimitError );
QUADRICMESHREDUCTION_API void PreserveSurfaceArea();
QUADRICMESHREDUCTION_API void ShrinkTriGroupWithMostSurfaceAreaLoss(float ShrinkAmount);
QUADRICMESHREDUCTION_API void DumpOBJ( const char* Filename );
QUADRICMESHREDUCTION_API void Compact();
uint32 GetRemainingNumVerts() const { return RemainingNumVerts; }
uint32 GetRemainingNumTris() const { return RemainingNumTris; }
int32 DegreeLimit = 24;
float DegreePenalty = 0.5f;
float LockPenalty = 1e8f;
float InversionPenalty = 100.0f;
protected:
uint32 NumVerts;
uint32 NumIndexes;
uint32 NumAttributes;
uint32 NumTris;
uint32 RemainingNumVerts;
uint32 RemainingNumTris;
float* Verts;
uint32* Indexes;
int32* MaterialIndexes;
const float* AttributeWeights = nullptr;
float EdgeWeight = 8.0f;
float MaxEdgeLengthFactor = 0.0f;
void (*CorrectAttributes)( float* ) = nullptr;
bool bLimitErrorToSurfaceArea = true;
bool bZeroWeights = false;
FHashTable VertHash;
FHashTable CornerHash;
TArray< uint32 > VertRefCount;
TArray< uint8 > CornerFlags;
TBitArray<> TriRemoved;
struct FPerMaterialDeltas
{
float SurfaceArea;
int32 NumTris;
int32 NumDisjoint;
};
TArray< FPerMaterialDeltas > PerMaterialDeltas;
struct FPair
{
FVector3f Position0;
FVector3f Position1;
};
TArray< FPair > Pairs;
FHashTable PairHash0;
FHashTable PairHash1;
FBinaryHeap< float > PairHeap;
TArray< uint32 > MovedVerts;
TArray< uint32 > MovedCorners;
TArray< uint32 > MovedPairs;
TArray< uint32 > ReevaluatePairs;
TArray64< uint8 > TriQuadrics;
TArray< FEdgeQuadric > EdgeQuadrics;
TBitArray<> EdgeQuadricsValid;
TArray< float > WedgeAttributes;
FDisjointSet WedgeDisjointSet;
enum ECornerFlags
{
MergeMask = 3, // Merge position 0 or 1
AdjTriMask = (1 << 2), // Has been added to AdjTris
LockedVertMask = (1 << 3), // Vert is locked, disallowing position movement
RemoveTriMask = (1 << 4), // Triangle will overlap another after merge and should be removed
};
protected:
FVector3f& GetPosition( uint32 VertIndex );
const FVector3f& GetPosition( uint32 VertIndex ) const;
float* GetAttributes( uint32 VertIndex );
FQuadricAttr& GetTriQuadric( uint32 TriIndex );
template< typename FuncType >
void ForAllVerts( const FVector3f& Position, FuncType&& Function ) const;
template< typename FuncType >
void ForAllCorners( const FVector3f& Position, FuncType&& Function ) const;
template< typename FuncType >
void ForAllPairs( const FVector3f& Position, FuncType&& Function ) const;
bool AddUniquePair( FPair& Pair, uint32 PairIndex );
void CalcTriQuadric( uint32 TriIndex );
void CalcEdgeQuadric( uint32 EdgeIndex );
float EvaluateMerge( const FVector3f& Position0, const FVector3f& Position1, bool bMoveVerts );
void BeginMovePosition( const FVector3f& Position );
void EndMovePositions();
uint32 CornerIndexMoved( uint32 TriIndex ) const;
bool TriWillInvert( uint32 TriIndex, const FVector3f& NewPosition ) const;
void RemoveTri( uint32 TriIndex );
void FixUpTri( uint32 TriIndex );
bool IsDuplicateTri( uint32 TriIndex ) const;
void SetVertIndex( uint32 Corner, uint32 NewVertIndex );
void RemoveDuplicateVerts( uint32 Corner );
};
FORCEINLINE FVector3f& FMeshSimplifier::GetPosition( uint32 VertIndex )
{
return *reinterpret_cast< FVector3f* >( &Verts[ ( 3 + NumAttributes ) * VertIndex ] );
}
FORCEINLINE const FVector3f& FMeshSimplifier::GetPosition( uint32 VertIndex ) const
{
return *reinterpret_cast< const FVector3f* >( &Verts[ ( 3 + NumAttributes ) * VertIndex ] );
}
FORCEINLINE float* FMeshSimplifier::GetAttributes( uint32 VertIndex )
{
return &Verts[ ( 3 + NumAttributes ) * VertIndex + 3 ];
}
FORCEINLINE FQuadricAttr& FMeshSimplifier::GetTriQuadric( uint32 TriIndex )
{
const SIZE_T QuadricSize = sizeof( FQuadricAttr ) + NumAttributes * 4 * sizeof( QScalar );
return *reinterpret_cast< FQuadricAttr* >( &TriQuadrics[ TriIndex * QuadricSize ] );
}
template< typename FuncType >
void FMeshSimplifier::ForAllVerts( const FVector3f& Position, FuncType&& Function ) const
{
uint32 Hash = HashPosition( Position );
for( uint32 VertIndex = VertHash.First( Hash ); VertHash.IsValid( VertIndex ); VertIndex = VertHash.Next( VertIndex ) )
{
if( GetPosition( VertIndex ) == Position )
{
Function( VertIndex );
}
}
}
template< typename FuncType >
void FMeshSimplifier::ForAllCorners( const FVector3f& Position, FuncType&& Function ) const
{
uint32 Hash = HashPosition( Position );
for( uint32 Corner = CornerHash.First( Hash ); CornerHash.IsValid( Corner ); Corner = CornerHash.Next( Corner ) )
{
if( GetPosition( Indexes[ Corner ] ) == Position )
{
Function( Corner );
}
}
}
template< typename FuncType >
void FMeshSimplifier::ForAllPairs( const FVector3f& Position, FuncType&& Function ) const
{
uint32 Hash = HashPosition( Position );
for( uint32 PairIndex = PairHash0.First( Hash ); PairHash0.IsValid( PairIndex ); PairIndex = PairHash0.Next( PairIndex ) )
{
if( Pairs[ PairIndex ].Position0 == Position )
{
Function( PairIndex );
}
}
for( uint32 PairIndex = PairHash1.First( Hash ); PairHash1.IsValid( PairIndex ); PairIndex = PairHash1.Next( PairIndex ) )
{
if( Pairs[ PairIndex ].Position1 == Position )
{
Function( PairIndex );
}
}
}