2018-02-22 10:04:16 -05:00
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
# include "MeshDescriptionOperations.h"
2018-02-22 11:20:51 -05:00
# include "UObject/Package.h"
2018-02-22 10:04:16 -05:00
# include "MeshDescription.h"
# include "MeshAttributes.h"
# include "RawMesh.h"
# include "RenderUtils.h"
# include "mikktspace.h"
2018-02-22 11:20:51 -05:00
# include "LayoutUV.h"
2018-02-22 10:04:16 -05:00
DEFINE_LOG_CATEGORY ( LogMeshDescriptionOperations ) ;
# define LOCTEXT_NAMESPACE "MeshDescriptionOperations"
//////////////////////////////////////////////////////////////////////////
// Local structure
struct FVertexInfo
{
FVertexInfo ( )
{
PolygonID = FPolygonID : : Invalid ;
VertexInstanceID = FVertexInstanceID : : Invalid ;
UVs = FVector2D ( 0.0f , 0.0f ) ;
EdgeIDs . Reserve ( 2 ) ; //Most of the time a edge has two triangles
}
FPolygonID PolygonID ;
FVertexInstanceID VertexInstanceID ;
FVector2D UVs ;
TArray < FEdgeID > EdgeIDs ;
} ;
2018-02-22 11:20:51 -05:00
/** Helper struct for building acceleration structures. */
2018-04-06 13:34:42 -04:00
namespace MeshDescriptionOperationNamespace
2018-02-22 11:20:51 -05:00
{
2018-04-06 13:34:42 -04:00
struct FIndexAndZ
2018-02-22 11:20:51 -05:00
{
2018-04-06 13:34:42 -04:00
float Z ;
int32 Index ;
const FVector * OriginalVector ;
/** Default constructor. */
FIndexAndZ ( ) { }
/** Initialization constructor. */
FIndexAndZ ( int32 InIndex , const FVector & V )
{
Z = 0.30f * V . X + 0.33f * V . Y + 0.37f * V . Z ;
Index = InIndex ;
OriginalVector = & V ;
}
} ;
/** Sorting function for vertex Z/index pairs. */
struct FCompareIndexAndZ
{
FORCEINLINE bool operator ( ) ( FIndexAndZ const & A , FIndexAndZ const & B ) const { return A . Z < B . Z ; }
} ;
}
2018-02-22 11:20:51 -05:00
2018-02-22 10:04:16 -05:00
//////////////////////////////////////////////////////////////////////////
// Converters
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : ConvertHardEdgesToSmoothGroup ( const FMeshDescription & SourceMeshDescription , FRawMesh & DestinationRawMesh )
2018-02-22 10:04:16 -05:00
{
TMap < FPolygonID , uint32 > PolygonSmoothGroup ;
2018-07-16 11:43:36 -04:00
PolygonSmoothGroup . Reserve ( SourceMeshDescription . Polygons ( ) . GetArraySize ( ) ) ;
2018-02-22 10:04:16 -05:00
TArray < bool > ConsumedPolygons ;
2018-07-16 11:43:36 -04:00
ConsumedPolygons . AddZeroed ( SourceMeshDescription . Polygons ( ) . GetArraySize ( ) ) ;
2018-02-22 10:04:16 -05:00
TMap < FPolygonID , uint32 > PolygonAvoidances ;
2018-07-16 11:43:36 -04:00
const TEdgeAttributeArray < bool > & EdgeHardnesses = SourceMeshDescription . EdgeAttributes ( ) . GetAttributes < bool > ( MeshAttribute : : Edge : : IsHard ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
for ( const FPolygonID PolygonID : SourceMeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
if ( ConsumedPolygons [ PolygonID . GetValue ( ) ] )
{
continue ;
}
TArray < FPolygonID > ConnectedPolygons ;
TArray < FPolygonID > LastConnectedPolygons ;
ConnectedPolygons . Add ( PolygonID ) ;
LastConnectedPolygons . Add ( FPolygonID : : Invalid ) ;
while ( ConnectedPolygons . Num ( ) > 0 )
{
check ( LastConnectedPolygons . Num ( ) = = ConnectedPolygons . Num ( ) ) ;
FPolygonID LastPolygonID = LastConnectedPolygons . Pop ( true ) ;
FPolygonID CurrentPolygonID = ConnectedPolygons . Pop ( true ) ;
if ( ConsumedPolygons [ CurrentPolygonID . GetValue ( ) ] )
{
continue ;
}
TArray < FPolygonID > SoftEdgeNeigbors ;
uint32 & SmoothGroup = PolygonSmoothGroup . FindOrAdd ( CurrentPolygonID ) ;
uint32 AvoidSmoothGroup = 0 ;
uint32 NeighborSmoothGroup = 0 ;
const uint32 LastSmoothGroupValue = ( LastPolygonID = = FPolygonID : : Invalid ) ? 0 : PolygonSmoothGroup [ LastPolygonID ] ;
TArray < FEdgeID > PolygonEdges ;
2018-07-16 11:43:36 -04:00
SourceMeshDescription . GetPolygonEdges ( CurrentPolygonID , PolygonEdges ) ;
2018-02-22 10:04:16 -05:00
for ( const FEdgeID & EdgeID : PolygonEdges )
{
bool bIsHardEdge = EdgeHardnesses [ EdgeID ] ;
2018-07-16 11:43:36 -04:00
const TArray < FPolygonID > & EdgeConnectedPolygons = SourceMeshDescription . GetEdgeConnectedPolygons ( EdgeID ) ;
2018-02-22 10:04:16 -05:00
for ( const FPolygonID & EdgePolygonID : EdgeConnectedPolygons )
{
if ( EdgePolygonID = = CurrentPolygonID )
{
continue ;
}
uint32 SmoothValue = 0 ;
if ( PolygonSmoothGroup . Contains ( EdgePolygonID ) )
{
SmoothValue = PolygonSmoothGroup [ EdgePolygonID ] ;
}
if ( bIsHardEdge ) //Hard Edge
{
AvoidSmoothGroup | = SmoothValue ;
}
else
{
NeighborSmoothGroup | = SmoothValue ;
//Put all none hard edge polygon in the next iteration
if ( ! ConsumedPolygons [ EdgePolygonID . GetValue ( ) ] )
{
ConnectedPolygons . Add ( EdgePolygonID ) ;
LastConnectedPolygons . Add ( CurrentPolygonID ) ;
}
else
{
SoftEdgeNeigbors . Add ( EdgePolygonID ) ;
}
}
}
}
if ( AvoidSmoothGroup ! = 0 )
{
PolygonAvoidances . FindOrAdd ( CurrentPolygonID ) = AvoidSmoothGroup ;
//find neighbor avoidance
for ( FPolygonID & NeighborID : SoftEdgeNeigbors )
{
if ( ! PolygonAvoidances . Contains ( NeighborID ) )
{
continue ;
}
AvoidSmoothGroup | = PolygonAvoidances [ NeighborID ] ;
}
uint32 NewSmoothGroup = 1 ;
while ( ( NewSmoothGroup & AvoidSmoothGroup ) ! = 0 & & NewSmoothGroup < MAX_uint32 )
{
//Shift the smooth group
NewSmoothGroup = NewSmoothGroup < < 1 ;
}
SmoothGroup = NewSmoothGroup ;
//Apply to all neighboard
for ( FPolygonID & NeighborID : SoftEdgeNeigbors )
{
PolygonSmoothGroup [ NeighborID ] | = NewSmoothGroup ;
}
}
else if ( NeighborSmoothGroup ! = 0 )
{
SmoothGroup | = LastSmoothGroupValue | NeighborSmoothGroup ;
}
else
{
SmoothGroup = 1 ;
}
ConsumedPolygons [ CurrentPolygonID . GetValue ( ) ] = true ;
}
}
//Now we have to put the data into the RawMesh
int32 TriangleIndex = 0 ;
2018-07-16 11:43:36 -04:00
for ( const FPolygonID PolygonID : SourceMeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
uint32 PolygonSmoothValue = PolygonSmoothGroup [ PolygonID ] ;
2018-07-16 11:43:36 -04:00
const TArray < FMeshTriangle > & Triangles = SourceMeshDescription . GetPolygonTriangles ( PolygonID ) ;
2018-02-22 10:04:16 -05:00
for ( const FMeshTriangle & MeshTriangle : Triangles )
{
DestinationRawMesh . FaceSmoothingMasks [ TriangleIndex + + ] = PolygonSmoothValue ;
}
}
}
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : ConvertSmoothGroupToHardEdges ( const FRawMesh & SourceRawMesh , FMeshDescription & DestinationMeshDescription )
2018-02-22 10:04:16 -05:00
{
2018-07-16 11:43:36 -04:00
TEdgeAttributeArray < bool > & EdgeHardnesses = DestinationMeshDescription . EdgeAttributes ( ) . GetAttributes < bool > ( MeshAttribute : : Edge : : IsHard ) ;
2018-02-22 10:04:16 -05:00
TArray < bool > ConsumedPolygons ;
2018-07-16 11:43:36 -04:00
ConsumedPolygons . AddZeroed ( DestinationMeshDescription . Polygons ( ) . Num ( ) ) ;
for ( const FPolygonID PolygonID : DestinationMeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
if ( ConsumedPolygons [ PolygonID . GetValue ( ) ] )
{
continue ;
}
TArray < FPolygonID > ConnectedPolygons ;
ConnectedPolygons . Add ( PolygonID ) ;
while ( ConnectedPolygons . Num ( ) > 0 )
{
FPolygonID CurrentPolygonID = ConnectedPolygons . Pop ( true ) ;
int32 CurrentPolygonIDValue = CurrentPolygonID . GetValue ( ) ;
check ( SourceRawMesh . FaceSmoothingMasks . IsValidIndex ( CurrentPolygonIDValue ) ) ;
const uint32 ReferenceSmoothGroup = SourceRawMesh . FaceSmoothingMasks [ CurrentPolygonIDValue ] ;
TArray < FEdgeID > PolygonEdges ;
2018-07-16 11:43:36 -04:00
DestinationMeshDescription . GetPolygonEdges ( CurrentPolygonID , PolygonEdges ) ;
2018-02-22 10:04:16 -05:00
for ( const FEdgeID & EdgeID : PolygonEdges )
{
const bool bIsHardEdge = EdgeHardnesses [ EdgeID ] ;
if ( bIsHardEdge )
{
continue ;
}
2018-07-16 11:43:36 -04:00
const TArray < FPolygonID > & EdgeConnectedPolygons = DestinationMeshDescription . GetEdgeConnectedPolygons ( EdgeID ) ;
2018-02-22 10:04:16 -05:00
for ( const FPolygonID & EdgePolygonID : EdgeConnectedPolygons )
{
int32 EdgePolygonIDValue = EdgePolygonID . GetValue ( ) ;
if ( EdgePolygonID = = CurrentPolygonID | | ConsumedPolygons [ EdgePolygonIDValue ] )
{
continue ;
}
check ( SourceRawMesh . FaceSmoothingMasks . IsValidIndex ( EdgePolygonIDValue ) ) ;
const uint32 TestSmoothGroup = SourceRawMesh . FaceSmoothingMasks [ EdgePolygonIDValue ] ;
if ( ( TestSmoothGroup & ReferenceSmoothGroup ) = = 0 )
{
EdgeHardnesses [ EdgeID ] = true ;
break ;
}
else
{
ConnectedPolygons . Add ( EdgePolygonID ) ;
}
}
}
ConsumedPolygons [ CurrentPolygonID . GetValue ( ) ] = true ;
}
}
}
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : ConvertToRawMesh ( const FMeshDescription & SourceMeshDescription , FRawMesh & DestinationRawMesh , const TMap < FName , int32 > & MaterialMap )
2018-02-22 10:04:16 -05:00
{
DestinationRawMesh . Empty ( ) ;
//Gather all array data
2018-07-16 11:43:36 -04:00
const TVertexAttributeArray < FVector > & VertexPositions = SourceMeshDescription . VertexAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Vertex : : Position ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
const TVertexInstanceAttributeArray < FVector > & VertexInstanceNormals = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : VertexInstance : : Normal ) ;
const TVertexInstanceAttributeArray < FVector > & VertexInstanceTangents = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : VertexInstance : : Tangent ) ;
const TVertexInstanceAttributeArray < float > & VertexInstanceBinormalSigns = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributes < float > ( MeshAttribute : : VertexInstance : : BinormalSign ) ;
const TVertexInstanceAttributeArray < FVector4 > & VertexInstanceColors = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector4 > ( MeshAttribute : : VertexInstance : : Color ) ;
const TVertexInstanceAttributeIndicesArray < FVector2D > & VertexInstanceUVs = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributesSet < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
const TPolygonGroupAttributeArray < FName > & PolygonGroupMaterialSlotName = SourceMeshDescription . PolygonGroupAttributes ( ) . GetAttributes < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
DestinationRawMesh . VertexPositions . AddZeroed ( SourceMeshDescription . Vertices ( ) . Num ( ) ) ;
2018-06-26 08:42:47 -04:00
TArray < int32 > RemapVerts ;
2018-07-16 11:43:36 -04:00
RemapVerts . AddZeroed ( SourceMeshDescription . Vertices ( ) . GetArraySize ( ) ) ;
2018-06-26 08:42:47 -04:00
int32 VertexIndex = 0 ;
2018-07-16 11:43:36 -04:00
for ( const FVertexID & VertexID : SourceMeshDescription . Vertices ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
2018-06-26 08:42:47 -04:00
DestinationRawMesh . VertexPositions [ VertexIndex ] = VertexPositions [ VertexID ] ;
RemapVerts [ VertexID . GetValue ( ) ] = VertexIndex ;
+ + VertexIndex ;
2018-02-22 10:04:16 -05:00
}
int32 TriangleNumber = 0 ;
2018-07-16 11:43:36 -04:00
for ( const FPolygonID & PolygonID : SourceMeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
2018-07-16 11:43:36 -04:00
TriangleNumber + = SourceMeshDescription . GetPolygonTriangles ( PolygonID ) . Num ( ) ;
2018-02-22 10:04:16 -05:00
}
DestinationRawMesh . FaceMaterialIndices . AddZeroed ( TriangleNumber ) ;
DestinationRawMesh . FaceSmoothingMasks . AddZeroed ( TriangleNumber ) ;
2018-06-26 08:42:47 -04:00
int32 WedgeIndexNumber = TriangleNumber * 3 ;
DestinationRawMesh . WedgeColors . AddZeroed ( WedgeIndexNumber ) ;
DestinationRawMesh . WedgeIndices . AddZeroed ( WedgeIndexNumber ) ;
DestinationRawMesh . WedgeTangentX . AddZeroed ( WedgeIndexNumber ) ;
DestinationRawMesh . WedgeTangentY . AddZeroed ( WedgeIndexNumber ) ;
DestinationRawMesh . WedgeTangentZ . AddZeroed ( WedgeIndexNumber ) ;
int32 ExistingUVCount = VertexInstanceUVs . GetNumIndices ( ) ;
for ( int32 UVIndex = 0 ; UVIndex < ExistingUVCount ; + + UVIndex )
{
DestinationRawMesh . WedgeTexCoords [ UVIndex ] . AddZeroed ( WedgeIndexNumber ) ;
}
2018-03-05 10:06:09 -05:00
int32 TriangleIndex = 0 ;
2018-06-26 08:42:47 -04:00
int32 WedgeIndex = 0 ;
2018-07-16 11:43:36 -04:00
for ( const FPolygonID PolygonID : SourceMeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
2018-07-16 11:43:36 -04:00
const FPolygonGroupID & PolygonGroupID = SourceMeshDescription . GetPolygonPolygonGroup ( PolygonID ) ;
2018-02-22 10:04:16 -05:00
int32 PolygonIDValue = PolygonID . GetValue ( ) ;
2018-07-16 11:43:36 -04:00
const TArray < FMeshTriangle > & Triangles = SourceMeshDescription . GetPolygonTriangles ( PolygonID ) ;
2018-02-22 10:04:16 -05:00
for ( const FMeshTriangle & MeshTriangle : Triangles )
{
2018-03-05 13:22:49 -05:00
if ( MaterialMap . Num ( ) > 0 & & MaterialMap . Contains ( PolygonGroupMaterialSlotName [ PolygonGroupID ] ) )
2018-03-05 10:06:09 -05:00
{
DestinationRawMesh . FaceMaterialIndices [ TriangleIndex ] = MaterialMap [ PolygonGroupMaterialSlotName [ PolygonGroupID ] ] ;
}
else
{
2018-03-05 13:22:49 -05:00
DestinationRawMesh . FaceMaterialIndices [ TriangleIndex ] = 0 ;
2018-03-05 10:06:09 -05:00
}
DestinationRawMesh . FaceSmoothingMasks [ TriangleIndex ] = 0 ; //Conversion of soft/hard to smooth mask is done after the geometry is converted
2018-02-22 10:04:16 -05:00
for ( int32 Corner = 0 ; Corner < 3 ; + + Corner )
{
const FVertexInstanceID VertexInstanceID = MeshTriangle . GetVertexInstanceID ( Corner ) ;
2018-06-26 08:42:47 -04:00
DestinationRawMesh . WedgeColors [ WedgeIndex ] = FLinearColor ( VertexInstanceColors [ VertexInstanceID ] ) . ToFColor ( true ) ;
2018-07-16 11:43:36 -04:00
DestinationRawMesh . WedgeIndices [ WedgeIndex ] = RemapVerts [ SourceMeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) . GetValue ( ) ] ;
2018-06-26 08:42:47 -04:00
DestinationRawMesh . WedgeTangentX [ WedgeIndex ] = VertexInstanceTangents [ VertexInstanceID ] ;
DestinationRawMesh . WedgeTangentY [ WedgeIndex ] = FVector : : CrossProduct ( VertexInstanceNormals [ VertexInstanceID ] , VertexInstanceTangents [ VertexInstanceID ] ) . GetSafeNormal ( ) * VertexInstanceBinormalSigns [ VertexInstanceID ] ;
DestinationRawMesh . WedgeTangentZ [ WedgeIndex ] = VertexInstanceNormals [ VertexInstanceID ] ;
2018-02-22 10:04:16 -05:00
for ( int32 UVIndex = 0 ; UVIndex < ExistingUVCount ; + + UVIndex )
{
2018-06-26 08:42:47 -04:00
DestinationRawMesh . WedgeTexCoords [ UVIndex ] [ WedgeIndex ] = VertexInstanceUVs . GetArrayForIndex ( UVIndex ) [ VertexInstanceID ] ;
2018-02-22 10:04:16 -05:00
}
2018-06-26 08:42:47 -04:00
+ + WedgeIndex ;
2018-02-22 10:04:16 -05:00
}
2018-03-05 10:06:09 -05:00
+ + TriangleIndex ;
2018-02-22 10:04:16 -05:00
}
}
//Convert the smoothgroup
ConvertHardEdgesToSmoothGroup ( SourceMeshDescription , DestinationRawMesh ) ;
}
2018-07-16 11:43:36 -04:00
//We want to fill the FMeshDescription vertex position mesh attribute with the FRawMesh vertex position
2018-03-29 16:39:17 -04:00
//We will also weld the vertex position (old FRawMesh is not always welded) and construct a mapping array to match the FVertexID
2018-07-16 11:43:36 -04:00
void FillMeshDescriptionVertexPositionNoDuplicate ( const TArray < FVector > & RawMeshVertexPositions , FMeshDescription & DestinationMeshDescription , TArray < FVertexID > & RemapVertexPosition )
2018-03-29 16:39:17 -04:00
{
2018-07-16 11:43:36 -04:00
TVertexAttributeArray < FVector > & VertexPositions = DestinationMeshDescription . VertexAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Vertex : : Position ) ;
2018-03-29 16:39:17 -04:00
const int32 NumVertex = RawMeshVertexPositions . Num ( ) ;
TMap < int32 , int32 > TempRemapVertexPosition ;
TempRemapVertexPosition . Reserve ( NumVertex ) ;
// Create a list of vertex Z/index pairs
2018-04-06 13:34:42 -04:00
TArray < MeshDescriptionOperationNamespace : : FIndexAndZ > VertIndexAndZ ;
2018-03-29 16:39:17 -04:00
VertIndexAndZ . Reserve ( NumVertex ) ;
for ( int32 VertexIndex = 0 ; VertexIndex < NumVertex ; + + VertexIndex )
{
2018-04-06 13:34:42 -04:00
new ( VertIndexAndZ ) MeshDescriptionOperationNamespace : : FIndexAndZ ( VertexIndex , RawMeshVertexPositions [ VertexIndex ] ) ;
2018-03-29 16:39:17 -04:00
}
// Sort the vertices by z value
2018-04-06 13:34:42 -04:00
VertIndexAndZ . Sort ( MeshDescriptionOperationNamespace : : FCompareIndexAndZ ( ) ) ;
2018-03-29 16:39:17 -04:00
int32 VertexCount = 0 ;
// Search for duplicates, quickly!
for ( int32 i = 0 ; i < VertIndexAndZ . Num ( ) ; i + + )
{
int32 Index_i = VertIndexAndZ [ i ] . Index ;
if ( TempRemapVertexPosition . Contains ( Index_i ) )
{
continue ;
}
TempRemapVertexPosition . FindOrAdd ( Index_i ) = VertexCount ;
// only need to search forward, since we add pairs both ways
2018-04-25 17:41:58 -04:00
for ( int32 j = i + 1 ; j < VertIndexAndZ . Num ( ) ; j + + )
2018-03-29 16:39:17 -04:00
{
if ( FMath : : Abs ( VertIndexAndZ [ j ] . Z - VertIndexAndZ [ i ] . Z ) > SMALL_NUMBER )
break ; // can't be any more dups
const FVector & PositionA = * ( VertIndexAndZ [ i ] . OriginalVector ) ;
const FVector & PositionB = * ( VertIndexAndZ [ j ] . OriginalVector ) ;
if ( PositionA . Equals ( PositionB , SMALL_NUMBER ) )
{
TempRemapVertexPosition . FindOrAdd ( VertIndexAndZ [ j ] . Index ) = VertexCount ;
}
2018-04-25 17:41:58 -04:00
}
2018-03-29 16:39:17 -04:00
VertexCount + + ;
}
//Make sure the vertex are added in the same order to be lossless when converting the FRawMesh
//In case there is a duplicate even reordering it will not be lossless, but MeshDescription do not support
//bad data like duplicated vertex position.
RemapVertexPosition . AddUninitialized ( NumVertex ) ;
2018-07-16 11:43:36 -04:00
DestinationMeshDescription . ReserveNewVertices ( VertexCount ) ;
2018-03-29 16:39:17 -04:00
TArray < FVertexID > UniqueVertexDone ;
UniqueVertexDone . AddUninitialized ( VertexCount ) ;
for ( int32 VertexIndex = 0 ; VertexIndex < VertexCount ; + + VertexIndex )
{
UniqueVertexDone [ VertexIndex ] = FVertexID : : Invalid ;
}
for ( int32 VertexIndex = 0 ; VertexIndex < NumVertex ; + + VertexIndex )
{
int32 RealIndex = TempRemapVertexPosition [ VertexIndex ] ;
if ( UniqueVertexDone [ RealIndex ] ! = FVertexID : : Invalid )
{
RemapVertexPosition [ VertexIndex ] = UniqueVertexDone [ RealIndex ] ;
continue ;
}
2018-07-16 11:43:36 -04:00
FVertexID VertexID = DestinationMeshDescription . CreateVertex ( ) ;
2018-03-29 16:39:17 -04:00
UniqueVertexDone [ RealIndex ] = VertexID ;
VertexPositions [ VertexID ] = RawMeshVertexPositions [ VertexIndex ] ;
RemapVertexPosition [ VertexIndex ] = VertexID ;
}
}
//Discover degenerated triangle
2018-07-16 11:43:36 -04:00
bool IsTriangleDegenerated ( const FRawMesh & SourceRawMesh , const TArray < FVertexID > & RemapVertexPosition , const int32 VerticeIndexBase )
2018-03-29 16:39:17 -04:00
{
FVertexID VertexIDs [ 3 ] ;
for ( int32 Corner = 0 ; Corner < 3 ; + + Corner )
{
int32 VerticeIndex = VerticeIndexBase + Corner ;
VertexIDs [ Corner ] = RemapVertexPosition [ SourceRawMesh . WedgeIndices [ VerticeIndex ] ] ;
}
return ( VertexIDs [ 0 ] = = VertexIDs [ 1 ] | | VertexIDs [ 0 ] = = VertexIDs [ 2 ] | | VertexIDs [ 1 ] = = VertexIDs [ 2 ] ) ;
}
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : ConvertFromRawMesh ( const FRawMesh & SourceRawMesh , FMeshDescription & DestinationMeshDescription , const TMap < int32 , FName > & MaterialMap )
2018-02-22 10:04:16 -05:00
{
2018-07-16 11:43:36 -04:00
DestinationMeshDescription . Empty ( ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
DestinationMeshDescription . ReserveNewVertexInstances ( SourceRawMesh . WedgeIndices . Num ( ) ) ;
DestinationMeshDescription . ReserveNewPolygons ( SourceRawMesh . WedgeIndices . Num ( ) / 3 ) ;
2018-03-29 16:39:17 -04:00
//Approximately 2.5 edges per polygons
2018-07-16 11:43:36 -04:00
DestinationMeshDescription . ReserveNewEdges ( SourceRawMesh . WedgeIndices . Num ( ) * 2.5f / 3 ) ;
2018-02-22 10:04:16 -05:00
2018-03-29 16:39:17 -04:00
//Gather all array data
2018-07-16 11:43:36 -04:00
TVertexInstanceAttributeArray < FVector > & VertexInstanceNormals = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : VertexInstance : : Normal ) ;
TVertexInstanceAttributeArray < FVector > & VertexInstanceTangents = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : VertexInstance : : Tangent ) ;
TVertexInstanceAttributeArray < float > & VertexInstanceBinormalSigns = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributes < float > ( MeshAttribute : : VertexInstance : : BinormalSign ) ;
TVertexInstanceAttributeArray < FVector4 > & VertexInstanceColors = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector4 > ( MeshAttribute : : VertexInstance : : Color ) ;
TVertexInstanceAttributeIndicesArray < FVector2D > & VertexInstanceUVs = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributesSet < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
TPolygonGroupAttributeArray < FName > & PolygonGroupImportedMaterialSlotNames = DestinationMeshDescription . PolygonGroupAttributes ( ) . GetAttributes < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
2018-02-22 10:04:16 -05:00
int32 NumTexCoords = 0 ;
int32 MaxTexCoords = MAX_MESH_TEXTURE_COORDS ;
TArray < int32 > TextureCoordinnateRemapIndex ;
TextureCoordinnateRemapIndex . AddZeroed ( MaxTexCoords ) ;
for ( int32 TextureCoordinnateIndex = 0 ; TextureCoordinnateIndex < MaxTexCoords ; + + TextureCoordinnateIndex )
{
TextureCoordinnateRemapIndex [ TextureCoordinnateIndex ] = INDEX_NONE ;
if ( SourceRawMesh . WedgeTexCoords [ TextureCoordinnateIndex ] . Num ( ) = = SourceRawMesh . WedgeIndices . Num ( ) )
{
TextureCoordinnateRemapIndex [ TextureCoordinnateIndex ] = NumTexCoords ;
NumTexCoords + + ;
}
}
VertexInstanceUVs . SetNumIndices ( NumTexCoords ) ;
2018-03-29 16:39:17 -04:00
//Ensure we do not have any duplicate, We found all duplicated vertex and compact them and build a remap indice array to remap the wedgeindices
TArray < FVertexID > RemapVertexPosition ;
FillMeshDescriptionVertexPositionNoDuplicate ( SourceRawMesh . VertexPositions , DestinationMeshDescription , RemapVertexPosition ) ;
2018-02-22 10:04:16 -05:00
bool bHasColors = SourceRawMesh . WedgeColors . Num ( ) > 0 ;
bool bHasTangents = SourceRawMesh . WedgeTangentX . Num ( ) > 0 & & SourceRawMesh . WedgeTangentY . Num ( ) > 0 ;
bool bHasNormals = SourceRawMesh . WedgeTangentZ . Num ( ) > 0 ;
TArray < FPolygonGroupID > PolygonGroups ;
2018-04-03 18:35:54 -04:00
TMap < int32 , FPolygonGroupID > MaterialIndexToPolygonGroup ;
2018-04-04 18:36:40 -04:00
//Create the PolygonGroups
2018-04-03 18:35:54 -04:00
for ( int32 MaterialIndex : SourceRawMesh . FaceMaterialIndices )
{
2018-04-04 18:36:40 -04:00
if ( ! MaterialIndexToPolygonGroup . Contains ( MaterialIndex ) )
2018-04-03 18:35:54 -04:00
{
2018-04-04 18:36:40 -04:00
FPolygonGroupID PolygonGroupID ( MaterialIndex ) ;
2018-07-16 11:43:36 -04:00
DestinationMeshDescription . CreatePolygonGroupWithID ( PolygonGroupID ) ;
2018-04-04 18:36:40 -04:00
PolygonGroupImportedMaterialSlotNames [ PolygonGroupID ] = FName ( * FString : : Printf ( TEXT ( " MaterialSlot_%d " ) , MaterialIndex ) ) ;
if ( MaterialMap . Contains ( MaterialIndex ) )
{
PolygonGroupImportedMaterialSlotNames [ PolygonGroupID ] = MaterialMap [ MaterialIndex ] ;
}
PolygonGroups . Add ( PolygonGroupID ) ;
MaterialIndexToPolygonGroup . Add ( MaterialIndex , PolygonGroupID ) ;
2018-04-03 18:35:54 -04:00
}
}
2018-02-22 10:04:16 -05:00
//Triangles
int32 TriangleCount = SourceRawMesh . WedgeIndices . Num ( ) / 3 ;
for ( int32 TriangleIndex = 0 ; TriangleIndex < TriangleCount ; + + TriangleIndex )
{
int32 VerticeIndexBase = TriangleIndex * 3 ;
2018-03-29 16:39:17 -04:00
//Check if the triangle is degenerated and skip the data if its the case
if ( IsTriangleDegenerated ( SourceRawMesh , RemapVertexPosition , VerticeIndexBase ) )
{
continue ;
}
2018-02-22 10:04:16 -05:00
//PolygonGroup
FPolygonGroupID PolygonGroupID = FPolygonGroupID : : Invalid ;
2018-03-05 10:06:09 -05:00
FName PolygonGroupImportedMaterialSlotName = NAME_None ;
2018-02-22 10:04:16 -05:00
int32 MaterialIndex = SourceRawMesh . FaceMaterialIndices [ TriangleIndex ] ;
2018-04-03 18:35:54 -04:00
if ( MaterialIndexToPolygonGroup . Contains ( MaterialIndex ) )
2018-02-22 10:04:16 -05:00
{
2018-04-03 18:35:54 -04:00
PolygonGroupID = MaterialIndexToPolygonGroup [ MaterialIndex ] ;
}
else if ( MaterialMap . Num ( ) > 0 & & MaterialMap . Contains ( MaterialIndex ) )
{
PolygonGroupImportedMaterialSlotName = MaterialMap [ MaterialIndex ] ;
2018-07-16 11:43:36 -04:00
for ( const FPolygonGroupID & SearchPolygonGroupID : DestinationMeshDescription . PolygonGroups ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
2018-03-05 10:06:09 -05:00
if ( PolygonGroupImportedMaterialSlotNames [ SearchPolygonGroupID ] = = PolygonGroupImportedMaterialSlotName )
{
PolygonGroupID = SearchPolygonGroupID ;
2018-04-03 18:35:54 -04:00
break ;
2018-03-05 10:06:09 -05:00
}
2018-02-22 10:04:16 -05:00
}
}
2018-03-05 10:06:09 -05:00
2018-02-22 10:04:16 -05:00
if ( PolygonGroupID = = FPolygonGroupID : : Invalid )
{
2018-07-16 11:43:36 -04:00
PolygonGroupID = DestinationMeshDescription . CreatePolygonGroup ( ) ;
2018-03-05 10:06:09 -05:00
PolygonGroupImportedMaterialSlotNames [ PolygonGroupID ] = PolygonGroupImportedMaterialSlotName = = NAME_None ? FName ( * FString : : Printf ( TEXT ( " MaterialSlot_%d " ) , MaterialIndex ) ) : PolygonGroupImportedMaterialSlotName ;
2018-02-22 10:04:16 -05:00
PolygonGroups . Add ( PolygonGroupID ) ;
2018-04-03 18:35:54 -04:00
MaterialIndexToPolygonGroup . Add ( MaterialIndex , PolygonGroupID ) ;
2018-02-22 10:04:16 -05:00
}
2018-03-29 16:39:17 -04:00
FVertexInstanceID TriangleVertexInstanceIDs [ 3 ] ;
2018-02-22 10:04:16 -05:00
for ( int32 Corner = 0 ; Corner < 3 ; + + Corner )
{
int32 VerticeIndex = VerticeIndexBase + Corner ;
2018-03-29 16:39:17 -04:00
FVertexID VertexID = RemapVertexPosition [ SourceRawMesh . WedgeIndices [ VerticeIndex ] ] ;
2018-07-16 11:43:36 -04:00
FVertexInstanceID VertexInstanceID = DestinationMeshDescription . CreateVertexInstance ( VertexID ) ;
2018-03-29 16:39:17 -04:00
TriangleVertexInstanceIDs [ Corner ] = VertexInstanceID ;
2018-02-22 10:04:16 -05:00
VertexInstanceColors [ VertexInstanceID ] = bHasColors ? FLinearColor : : FromSRGBColor ( SourceRawMesh . WedgeColors [ VerticeIndex ] ) : FLinearColor : : White ;
VertexInstanceTangents [ VertexInstanceID ] = bHasTangents ? SourceRawMesh . WedgeTangentX [ VerticeIndex ] : FVector ( ForceInitToZero ) ;
VertexInstanceBinormalSigns [ VertexInstanceID ] = bHasTangents ? GetBasisDeterminantSign ( SourceRawMesh . WedgeTangentX [ VerticeIndex ] . GetSafeNormal ( ) , SourceRawMesh . WedgeTangentY [ VerticeIndex ] . GetSafeNormal ( ) , SourceRawMesh . WedgeTangentZ [ VerticeIndex ] . GetSafeNormal ( ) ) : 0.0f ;
VertexInstanceNormals [ VertexInstanceID ] = bHasNormals ? SourceRawMesh . WedgeTangentZ [ VerticeIndex ] : FVector ( ForceInitToZero ) ;
for ( int32 TextureCoordinnateIndex = 0 ; TextureCoordinnateIndex < NumTexCoords ; + + TextureCoordinnateIndex )
{
int32 TextureCoordIndex = TextureCoordinnateRemapIndex [ TextureCoordinnateIndex ] ;
if ( TextureCoordIndex = = INDEX_NONE )
{
continue ;
}
TMeshAttributeArray < FVector2D , FVertexInstanceID > & Uvs = VertexInstanceUVs . GetArrayForIndex ( TextureCoordIndex ) ;
Uvs [ VertexInstanceID ] = SourceRawMesh . WedgeTexCoords [ TextureCoordinnateIndex ] [ VerticeIndex ] ;
}
}
//Create the polygon edges
2018-07-16 11:43:36 -04:00
TArray < FMeshDescription : : FContourPoint > Contours ;
2018-02-22 10:04:16 -05:00
for ( uint32 Corner = 0 ; Corner < 3 ; + + Corner )
{
int32 ContourPointIndex = Contours . AddDefaulted ( ) ;
2018-07-16 11:43:36 -04:00
FMeshDescription : : FContourPoint & ContourPoint = Contours [ ContourPointIndex ] ;
2018-02-22 10:04:16 -05:00
//Find the matching edge ID
int32 CornerIndices [ 2 ] ;
CornerIndices [ 0 ] = ( Corner + 0 ) % 3 ;
CornerIndices [ 1 ] = ( Corner + 1 ) % 3 ;
FVertexID EdgeVertexIDs [ 2 ] ;
2018-07-16 11:43:36 -04:00
EdgeVertexIDs [ 0 ] = DestinationMeshDescription . GetVertexInstanceVertex ( FVertexInstanceID ( TriangleVertexInstanceIDs [ CornerIndices [ 0 ] ] ) ) ;
EdgeVertexIDs [ 1 ] = DestinationMeshDescription . GetVertexInstanceVertex ( FVertexInstanceID ( TriangleVertexInstanceIDs [ CornerIndices [ 1 ] ] ) ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
FEdgeID MatchEdgeId = DestinationMeshDescription . GetVertexPairEdge ( EdgeVertexIDs [ 0 ] , EdgeVertexIDs [ 1 ] ) ;
2018-02-22 10:04:16 -05:00
if ( MatchEdgeId = = FEdgeID : : Invalid )
{
2018-07-16 11:43:36 -04:00
MatchEdgeId = DestinationMeshDescription . CreateEdge ( EdgeVertexIDs [ 0 ] , EdgeVertexIDs [ 1 ] ) ;
2018-02-22 10:04:16 -05:00
}
ContourPoint . EdgeID = MatchEdgeId ;
2018-03-29 16:39:17 -04:00
ContourPoint . VertexInstanceID = FVertexInstanceID ( TriangleVertexInstanceIDs [ CornerIndices [ 0 ] ] ) ;
2018-02-22 10:04:16 -05:00
}
2018-07-16 11:43:36 -04:00
const FPolygonID NewPolygonID = DestinationMeshDescription . CreatePolygon ( PolygonGroupID , Contours ) ;
int32 NewTriangleIndex = DestinationMeshDescription . GetPolygonTriangles ( NewPolygonID ) . AddDefaulted ( ) ;
FMeshTriangle & NewTriangle = DestinationMeshDescription . GetPolygonTriangles ( NewPolygonID ) [ NewTriangleIndex ] ;
2018-02-22 10:04:16 -05:00
for ( int32 Corner = 0 ; Corner < 3 ; + + Corner )
{
2018-03-29 16:39:17 -04:00
FVertexInstanceID VertexInstanceID = TriangleVertexInstanceIDs [ Corner ] ;
2018-02-22 10:04:16 -05:00
NewTriangle . SetVertexInstanceID ( Corner , VertexInstanceID ) ;
}
}
2018-04-20 18:47:04 -04:00
2018-03-29 16:39:17 -04:00
ConvertSmoothGroupToHardEdges ( SourceRawMesh , DestinationMeshDescription ) ;
2018-02-22 10:04:16 -05:00
2018-03-22 20:14:49 -04:00
//Create the missing normals and tangents, should we use Mikkt space for tangent???
if ( ! bHasNormals | | ! bHasTangents )
2018-03-22 16:02:58 -04:00
{
2018-07-16 11:43:36 -04:00
//DestinationMeshDescription.ComputePolygonTangentsAndNormals(0.0f);
2018-04-20 18:47:04 -04:00
FMeshDescriptionOperations : : CreatePolygonNTB ( DestinationMeshDescription , 0.0f ) ;
2018-03-29 16:39:17 -04:00
//EComputeNTBsOptions ComputeNTBsOptions = (bHasNormals ? EComputeNTBsOptions::None : EComputeNTBsOptions::Normals) | (bHasTangents ? EComputeNTBsOptions::None : EComputeNTBsOptions::Tangents);
2018-07-16 11:43:36 -04:00
//DestinationMeshDescription.ComputeTangentsAndNormals(ComputeNTBsOptions);
2018-03-29 16:39:17 -04:00
//Create the missing normals and tangents
if ( ! bHasNormals )
{
CreateNormals ( DestinationMeshDescription , ETangentOptions : : BlendOverlappingNormals , false ) ;
}
CreateMikktTangents ( DestinationMeshDescription , ETangentOptions : : BlendOverlappingNormals ) ;
2018-02-22 10:04:16 -05:00
}
}
//////////////////////////////////////////////////////////////////////////
// Normals tangents and Bi-normals
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : CreatePolygonNTB ( FMeshDescription & MeshDescription , float ComparisonThreshold )
2018-02-22 10:04:16 -05:00
{
2018-07-16 11:43:36 -04:00
const TVertexAttributeArray < FVector > & VertexPositions = MeshDescription . VertexAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Vertex : : Position ) ;
TVertexInstanceAttributeArray < FVector2D > & VertexUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate , 0 ) ;
TPolygonAttributeArray < FVector > & PolygonNormals = MeshDescription . PolygonAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Polygon : : Normal ) ;
TPolygonAttributeArray < FVector > & PolygonTangents = MeshDescription . PolygonAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Polygon : : Tangent ) ;
TPolygonAttributeArray < FVector > & PolygonBinormals = MeshDescription . PolygonAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Polygon : : Binormal ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
FVertexInstanceArray & VertexInstanceArray = MeshDescription . VertexInstances ( ) ;
FVertexArray & VertexArray = MeshDescription . Vertices ( ) ;
FPolygonArray & PolygonArray = MeshDescription . Polygons ( ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
for ( const FPolygonID PolygonID : MeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
if ( ! PolygonNormals [ PolygonID ] . IsNearlyZero ( ) )
{
//By pass normal calculation if its already done
continue ;
}
2018-07-16 11:43:36 -04:00
const TArray < FMeshTriangle > & MeshTriangles = MeshDescription . GetPolygonTriangles ( PolygonID ) ;
2018-03-21 10:54:37 -04:00
FVector TangentX ( 0.0f ) ;
FVector TangentY ( 0.0f ) ;
FVector TangentZ ( 0.0f ) ;
for ( const FMeshTriangle & MeshTriangle : MeshTriangles )
2018-02-22 10:04:16 -05:00
{
2018-03-21 10:54:37 -04:00
int32 UVIndex = 0 ;
FVector P [ 3 ] ;
FVector2D UVs [ 3 ] ;
for ( int32 i = 0 ; i < 3 ; + + i )
{
const FVertexInstanceID VertexInstanceID = MeshTriangle . GetVertexInstanceID ( i ) ;
UVs [ i ] = VertexUVs [ VertexInstanceID ] ;
2018-07-16 11:43:36 -04:00
P [ i ] = VertexPositions [ MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) ] ;
2018-03-21 10:54:37 -04:00
}
const FVector Normal = ( ( P [ 1 ] - P [ 2 ] ) ^ ( P [ 0 ] - P [ 2 ] ) ) . GetSafeNormal ( ComparisonThreshold ) ;
//Check for degenerated polygons, avoid NAN
if ( ! Normal . IsNearlyZero ( ComparisonThreshold ) )
{
FMatrix ParameterToLocal (
FPlane ( P [ 1 ] . X - P [ 0 ] . X , P [ 1 ] . Y - P [ 0 ] . Y , P [ 1 ] . Z - P [ 0 ] . Z , 0 ) ,
FPlane ( P [ 2 ] . X - P [ 0 ] . X , P [ 2 ] . Y - P [ 0 ] . Y , P [ 2 ] . Z - P [ 0 ] . Z , 0 ) ,
FPlane ( P [ 0 ] . X , P [ 0 ] . Y , P [ 0 ] . Z , 0 ) ,
FPlane ( 0 , 0 , 0 , 1 )
) ;
FMatrix ParameterToTexture (
FPlane ( UVs [ 1 ] . X - UVs [ 0 ] . X , UVs [ 1 ] . Y - UVs [ 0 ] . Y , 0 , 0 ) ,
FPlane ( UVs [ 2 ] . X - UVs [ 0 ] . X , UVs [ 2 ] . Y - UVs [ 0 ] . Y , 0 , 0 ) ,
FPlane ( UVs [ 0 ] . X , UVs [ 0 ] . Y , 1 , 0 ) ,
FPlane ( 0 , 0 , 0 , 1 )
) ;
// Use InverseSlow to catch singular matrices. Inverse can miss this sometimes.
const FMatrix TextureToLocal = ParameterToTexture . Inverse ( ) * ParameterToLocal ;
FVector TmpTangentX ( 0.0f ) ;
FVector TmpTangentY ( 0.0f ) ;
FVector TmpTangentZ ( 0.0f ) ;
TmpTangentX = TextureToLocal . TransformVector ( FVector ( 1 , 0 , 0 ) ) . GetSafeNormal ( ) ;
TmpTangentY = TextureToLocal . TransformVector ( FVector ( 0 , 1 , 0 ) ) . GetSafeNormal ( ) ;
TmpTangentZ = Normal ;
FVector : : CreateOrthonormalBasis ( TmpTangentX , TmpTangentY , TmpTangentZ ) ;
TangentX + = TmpTangentX ;
TangentY + = TmpTangentY ;
TangentZ + = TmpTangentZ ;
}
else
{
2018-04-20 18:47:04 -04:00
//This will force a recompute of the normals and tangents
TangentX = FVector ( 0.0f ) ;
TangentY = FVector ( 0.0f ) ;
TangentZ = FVector ( 0.0f ) ;
break ;
2018-03-21 10:54:37 -04:00
}
2018-02-22 10:04:16 -05:00
}
2018-03-21 10:54:37 -04:00
TangentX . Normalize ( ) ;
TangentY . Normalize ( ) ;
TangentZ . Normalize ( ) ;
2018-02-22 10:04:16 -05:00
PolygonTangents [ PolygonID ] = TangentX ;
PolygonBinormals [ PolygonID ] = TangentY ;
PolygonNormals [ PolygonID ] = TangentZ ;
}
}
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : CreateNormals ( FMeshDescription & MeshDescription , FMeshDescriptionOperations : : ETangentOptions TangentOptions , bool bComputeTangent )
2018-02-22 10:04:16 -05:00
{
//For each vertex compute the normals for every connected edges that are smooth betwween hard edges
// H A B
// \ || /
// G -- ** -- C
/ / / / | \
// F E D
//
// The double ** are the vertex, the double line are hard edges, the single line are soft edge.
// A and F are hard, all other edges are soft. The goal is to compute two average normals one from
// A to F and a second from F to A. Then we can set the vertex instance normals accordingly.
// First normal(A to F) = Normalize(A+B+C+D+E+F)
// Second normal(F to A) = Normalize(F+G+H+A)
// We found the connected edge using the triangle that share edges
// @todo: provide an option to weight each contributing polygon normal according to the size of
// the angle it makes with the vertex being calculated. This means that triangulated faces whose
// internal edge meets the vertex doesn't get undue extra weight.
2018-07-16 11:43:36 -04:00
const TVertexInstanceAttributeArray < FVector2D > & VertexUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate , 0 ) ;
TVertexInstanceAttributeArray < FVector > & VertexNormals = MeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : VertexInstance : : Normal , 0 ) ;
TVertexInstanceAttributeArray < FVector > & VertexTangents = MeshDescription . VertexInstanceAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : VertexInstance : : Tangent , 0 ) ;
TVertexInstanceAttributeArray < float > & VertexBinormalSigns = MeshDescription . VertexInstanceAttributes ( ) . GetAttributes < float > ( MeshAttribute : : VertexInstance : : BinormalSign , 0 ) ;
2018-02-22 10:04:16 -05:00
2018-07-16 11:43:36 -04:00
TPolygonAttributeArray < FVector > & PolygonNormals = MeshDescription . PolygonAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Polygon : : Normal ) ;
TPolygonAttributeArray < FVector > & PolygonTangents = MeshDescription . PolygonAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Polygon : : Tangent ) ;
TPolygonAttributeArray < FVector > & PolygonBinormals = MeshDescription . PolygonAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Polygon : : Binormal ) ;
2018-02-22 10:04:16 -05:00
TMap < FPolygonID , FVertexInfo > VertexInfoMap ;
VertexInfoMap . Reserve ( 20 ) ;
//Iterate all vertex to compute normals for all vertex instance
2018-07-16 11:43:36 -04:00
for ( const FVertexID VertexID : MeshDescription . Vertices ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
VertexInfoMap . Reset ( ) ;
bool bPointHasAllTangents = true ;
//Fill the VertexInfoMap
2018-07-16 11:43:36 -04:00
for ( const FEdgeID EdgeID : MeshDescription . GetVertexConnectedEdges ( VertexID ) )
2018-02-22 10:04:16 -05:00
{
2018-07-16 11:43:36 -04:00
for ( const FPolygonID PolygonID : MeshDescription . GetEdgeConnectedPolygons ( EdgeID ) )
2018-02-22 10:04:16 -05:00
{
FVertexInfo & VertexInfo = VertexInfoMap . FindOrAdd ( PolygonID ) ;
int32 EdgeIndex = VertexInfo . EdgeIDs . AddUnique ( EdgeID ) ;
if ( VertexInfo . PolygonID = = FPolygonID : : Invalid )
{
VertexInfo . PolygonID = PolygonID ;
2018-07-16 11:43:36 -04:00
for ( const FVertexInstanceID VertexInstanceID : MeshDescription . GetPolygonPerimeterVertexInstances ( PolygonID ) )
2018-02-22 10:04:16 -05:00
{
2018-07-16 11:43:36 -04:00
if ( MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) = = VertexID )
2018-02-22 10:04:16 -05:00
{
VertexInfo . VertexInstanceID = VertexInstanceID ;
VertexInfo . UVs = VertexUVs [ VertexInstanceID ] ;
bPointHasAllTangents & = ! VertexNormals [ VertexInstanceID ] . IsNearlyZero ( ) & & ! VertexTangents [ VertexInstanceID ] . IsNearlyZero ( ) ;
2018-04-23 18:11:15 -04:00
if ( bPointHasAllTangents )
{
FVector TangentX = VertexTangents [ VertexInstanceID ] . GetSafeNormal ( ) ;
FVector TangentZ = VertexNormals [ VertexInstanceID ] . GetSafeNormal ( ) ;
FVector TangentY = ( FVector : : CrossProduct ( TangentZ , TangentX ) . GetSafeNormal ( ) * VertexBinormalSigns [ VertexInstanceID ] ) . GetSafeNormal ( ) ;
if ( TangentX . ContainsNaN ( ) | | TangentX . IsNearlyZero ( SMALL_NUMBER ) | |
TangentY . ContainsNaN ( ) | | TangentY . IsNearlyZero ( SMALL_NUMBER ) | |
TangentZ . ContainsNaN ( ) | | TangentZ . IsNearlyZero ( SMALL_NUMBER ) )
{
bPointHasAllTangents = false ;
}
}
2018-02-22 10:04:16 -05:00
break ;
}
}
}
}
}
if ( bPointHasAllTangents )
{
continue ;
}
//Build all group by recursively traverse all polygon connected to the vertex
TArray < TArray < FPolygonID > > Groups ;
TArray < FPolygonID > ConsumedPolygon ;
for ( auto Kvp : VertexInfoMap )
{
if ( ConsumedPolygon . Contains ( Kvp . Key ) )
{
continue ;
}
int32 CurrentGroupIndex = Groups . AddZeroed ( ) ;
TArray < FPolygonID > & CurrentGroup = Groups [ CurrentGroupIndex ] ;
TArray < FPolygonID > PolygonQueue ;
PolygonQueue . Add ( Kvp . Key ) ; //Use a queue to avoid recursive function
while ( PolygonQueue . Num ( ) > 0 )
{
FPolygonID CurrentPolygonID = PolygonQueue . Pop ( true ) ;
FVertexInfo & CurrentVertexInfo = VertexInfoMap . FindOrAdd ( CurrentPolygonID ) ;
CurrentGroup . AddUnique ( CurrentVertexInfo . PolygonID ) ;
ConsumedPolygon . AddUnique ( CurrentVertexInfo . PolygonID ) ;
2018-07-16 11:43:36 -04:00
const TEdgeAttributeArray < bool > & EdgeHardnesses = MeshDescription . EdgeAttributes ( ) . GetAttributes < bool > ( MeshAttribute : : Edge : : IsHard ) ;
2018-02-22 10:04:16 -05:00
for ( const FEdgeID EdgeID : CurrentVertexInfo . EdgeIDs )
{
if ( EdgeHardnesses [ EdgeID ] )
{
//End of the group
continue ;
}
2018-07-16 11:43:36 -04:00
for ( const FPolygonID PolygonID : MeshDescription . GetEdgeConnectedPolygons ( EdgeID ) )
2018-02-22 10:04:16 -05:00
{
if ( PolygonID = = CurrentVertexInfo . PolygonID )
{
continue ;
}
//Add this polygon to the group
FVertexInfo & OtherVertexInfo = VertexInfoMap . FindOrAdd ( PolygonID ) ;
//Do not repeat polygons
if ( ! ConsumedPolygon . Contains ( OtherVertexInfo . PolygonID ) )
{
PolygonQueue . Add ( PolygonID ) ;
}
}
}
}
}
//Smooth every connected group
ConsumedPolygon . Reset ( ) ;
for ( const TArray < FPolygonID > & Group : Groups )
{
//Compute tangents data
TMap < FVector2D , FVector > GroupTangent ;
TMap < FVector2D , FVector > GroupBiNormal ;
TArray < FVertexInstanceID > VertexInstanceInGroup ;
FVector GroupNormal ( 0.0f ) ;
for ( const FPolygonID PolygonID : Group )
{
2018-04-23 18:11:15 -04:00
FVector PolyNormal = PolygonNormals [ PolygonID ] ;
FVector PolyTangent = PolygonTangents [ PolygonID ] ;
FVector PolyBinormal = PolygonBinormals [ PolygonID ] ;
2018-02-22 10:04:16 -05:00
ConsumedPolygon . Add ( PolygonID ) ;
VertexInstanceInGroup . Add ( VertexInfoMap [ PolygonID ] . VertexInstanceID ) ;
2018-04-23 18:11:15 -04:00
if ( ! PolyNormal . IsNearlyZero ( SMALL_NUMBER ) & & ! PolyNormal . ContainsNaN ( ) )
{
GroupNormal + = PolyNormal ;
}
2018-02-22 10:04:16 -05:00
if ( bComputeTangent )
{
const FVector2D UVs = VertexInfoMap [ PolygonID ] . UVs ;
bool CreateGroup = ( ! GroupTangent . Contains ( UVs ) ) ;
FVector & GroupTangentValue = GroupTangent . FindOrAdd ( UVs ) ;
FVector & GroupBiNormalValue = GroupBiNormal . FindOrAdd ( UVs ) ;
2018-04-23 18:11:15 -04:00
if ( CreateGroup )
{
GroupTangentValue = FVector ( 0.0f ) ;
GroupBiNormalValue = FVector ( 0.0f ) ;
}
if ( ! PolyTangent . IsNearlyZero ( SMALL_NUMBER ) & & ! PolyTangent . ContainsNaN ( ) )
{
GroupTangentValue + = PolyTangent ;
}
if ( ! PolyBinormal . IsNearlyZero ( SMALL_NUMBER ) & & ! PolyBinormal . ContainsNaN ( ) )
{
GroupBiNormalValue + = PolyBinormal ;
}
2018-02-22 10:04:16 -05:00
}
}
//////////////////////////////////////////////////////////////////////////
//Apply the group to the Mesh
GroupNormal . Normalize ( ) ;
if ( bComputeTangent )
{
for ( auto Kvp : GroupTangent )
{
FVector & GroupTangentValue = GroupTangent . FindOrAdd ( Kvp . Key ) ;
GroupTangentValue . Normalize ( ) ;
}
for ( auto Kvp : GroupBiNormal )
{
FVector & GroupBiNormalValue = GroupBiNormal . FindOrAdd ( Kvp . Key ) ;
GroupBiNormalValue . Normalize ( ) ;
}
}
//Apply the average NTB on all Vertex instance
for ( const FVertexInstanceID VertexInstanceID : VertexInstanceInGroup )
{
const FVector2D VertexUV = VertexUVs [ VertexInstanceID ] ;
if ( VertexNormals [ VertexInstanceID ] . IsNearlyZero ( SMALL_NUMBER ) )
{
VertexNormals [ VertexInstanceID ] = GroupNormal ;
}
if ( bComputeTangent )
{
//Avoid changing the original group value
FVector GroupTangentValue = GroupTangent [ VertexUV ] ;
FVector GroupBiNormalValue = GroupBiNormal [ VertexUV ] ;
if ( ! VertexTangents [ VertexInstanceID ] . IsNearlyZero ( SMALL_NUMBER ) )
{
GroupTangentValue = VertexTangents [ VertexInstanceID ] ;
}
FVector BiNormal ( 0.0f ) ;
if ( ! VertexNormals [ VertexInstanceID ] . IsNearlyZero ( SMALL_NUMBER ) & & ! VertexTangents [ VertexInstanceID ] . IsNearlyZero ( SMALL_NUMBER ) )
{
BiNormal = FVector : : CrossProduct ( VertexNormals [ VertexInstanceID ] , VertexTangents [ VertexInstanceID ] ) . GetSafeNormal ( ) * VertexBinormalSigns [ VertexInstanceID ] ;
}
if ( ! BiNormal . IsNearlyZero ( SMALL_NUMBER ) )
{
GroupBiNormalValue = BiNormal ;
}
// Gram-Schmidt orthogonalization
GroupBiNormalValue - = GroupTangentValue * ( GroupTangentValue | GroupBiNormalValue ) ;
GroupBiNormalValue . Normalize ( ) ;
GroupTangentValue - = VertexNormals [ VertexInstanceID ] * ( VertexNormals [ VertexInstanceID ] | GroupTangentValue ) ;
GroupTangentValue . Normalize ( ) ;
GroupBiNormalValue - = VertexNormals [ VertexInstanceID ] * ( VertexNormals [ VertexInstanceID ] | GroupBiNormalValue ) ;
GroupBiNormalValue . Normalize ( ) ;
//Set the value
VertexTangents [ VertexInstanceID ] = GroupTangentValue ;
//If the BiNormal is zero set the sign to 1.0f
VertexBinormalSigns [ VertexInstanceID ] = GetBasisDeterminantSign ( GroupTangentValue , GroupBiNormalValue , VertexNormals [ VertexInstanceID ] ) ;
}
}
}
}
}
namespace MeshDescriptionMikktSpaceInterface
{
//Mikk t spce static function
int MikkGetNumFaces ( const SMikkTSpaceContext * Context ) ;
int MikkGetNumVertsOfFace ( const SMikkTSpaceContext * Context , const int FaceIdx ) ;
void MikkGetPosition ( const SMikkTSpaceContext * Context , float Position [ 3 ] , const int FaceIdx , const int VertIdx ) ;
void MikkGetNormal ( const SMikkTSpaceContext * Context , float Normal [ 3 ] , const int FaceIdx , const int VertIdx ) ;
void MikkSetTSpaceBasic ( const SMikkTSpaceContext * Context , const float Tangent [ 3 ] , const float BitangentSign , const int FaceIdx , const int VertIdx ) ;
void MikkGetTexCoord ( const SMikkTSpaceContext * Context , float UV [ 2 ] , const int FaceIdx , const int VertIdx ) ;
}
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : CreateMikktTangents ( FMeshDescription & MeshDescription , FMeshDescriptionOperations : : ETangentOptions TangentOptions )
2018-02-22 10:04:16 -05:00
{
bool bIgnoreDegenerateTriangles = ( TangentOptions & FMeshDescriptionOperations : : ETangentOptions : : IgnoreDegenerateTriangles ) ! = 0 ;
// we can use mikktspace to calculate the tangents
SMikkTSpaceInterface MikkTInterface ;
MikkTInterface . m_getNormal = MeshDescriptionMikktSpaceInterface : : MikkGetNormal ;
MikkTInterface . m_getNumFaces = MeshDescriptionMikktSpaceInterface : : MikkGetNumFaces ;
MikkTInterface . m_getNumVerticesOfFace = MeshDescriptionMikktSpaceInterface : : MikkGetNumVertsOfFace ;
MikkTInterface . m_getPosition = MeshDescriptionMikktSpaceInterface : : MikkGetPosition ;
MikkTInterface . m_getTexCoord = MeshDescriptionMikktSpaceInterface : : MikkGetTexCoord ;
MikkTInterface . m_setTSpaceBasic = MeshDescriptionMikktSpaceInterface : : MikkSetTSpaceBasic ;
MikkTInterface . m_setTSpace = nullptr ;
SMikkTSpaceContext MikkTContext ;
MikkTContext . m_pInterface = & MikkTInterface ;
2018-07-16 11:43:36 -04:00
MikkTContext . m_pUserData = ( void * ) ( & MeshDescription ) ;
2018-02-22 10:04:16 -05:00
MikkTContext . m_bIgnoreDegenerates = bIgnoreDegenerateTriangles ;
genTangSpaceDefault ( & MikkTContext ) ;
}
namespace MeshDescriptionMikktSpaceInterface
{
int MikkGetNumFaces ( const SMikkTSpaceContext * Context )
{
2018-08-16 13:53:43 -04:00
FMeshDescription * MeshDescription = ( FMeshDescription * ) ( Context - > m_pUserData ) ;
return MeshDescription - > Polygons ( ) . GetArraySize ( ) ;
2018-02-22 10:04:16 -05:00
}
int MikkGetNumVertsOfFace ( const SMikkTSpaceContext * Context , const int FaceIdx )
{
// All of our meshes are triangles.
2018-08-16 13:53:43 -04:00
FMeshDescription * MeshDescription = ( FMeshDescription * ) ( Context - > m_pUserData ) ;
if ( MeshDescription - > IsPolygonValid ( FPolygonID ( FaceIdx ) ) )
{
const FMeshPolygon & Polygon = MeshDescription - > GetPolygon ( FPolygonID ( FaceIdx ) ) ;
return Polygon . PerimeterContour . VertexInstanceIDs . Num ( ) ;
}
return 0 ;
2018-02-22 10:04:16 -05:00
}
void MikkGetPosition ( const SMikkTSpaceContext * Context , float Position [ 3 ] , const int FaceIdx , const int VertIdx )
{
2018-07-16 11:43:36 -04:00
FMeshDescription * MeshDescription = ( FMeshDescription * ) ( Context - > m_pUserData ) ;
2018-03-21 10:54:37 -04:00
const FMeshPolygon & Polygon = MeshDescription - > GetPolygon ( FPolygonID ( FaceIdx ) ) ;
const FVertexInstanceID VertexInstanceID = Polygon . PerimeterContour . VertexInstanceIDs [ VertIdx ] ;
2018-02-22 10:04:16 -05:00
const FVertexID VertexID = MeshDescription - > GetVertexInstanceVertex ( VertexInstanceID ) ;
const FVector & VertexPosition = MeshDescription - > VertexAttributes ( ) . GetAttribute < FVector > ( VertexID , MeshAttribute : : Vertex : : Position ) ;
Position [ 0 ] = VertexPosition . X ;
Position [ 1 ] = VertexPosition . Y ;
Position [ 2 ] = VertexPosition . Z ;
}
void MikkGetNormal ( const SMikkTSpaceContext * Context , float Normal [ 3 ] , const int FaceIdx , const int VertIdx )
{
2018-07-16 11:43:36 -04:00
FMeshDescription * MeshDescription = ( FMeshDescription * ) ( Context - > m_pUserData ) ;
2018-03-21 10:54:37 -04:00
const FMeshPolygon & Polygon = MeshDescription - > GetPolygon ( FPolygonID ( FaceIdx ) ) ;
const FVertexInstanceID VertexInstanceID = Polygon . PerimeterContour . VertexInstanceIDs [ VertIdx ] ;
2018-02-22 10:04:16 -05:00
const FVector & VertexNormal = MeshDescription - > VertexInstanceAttributes ( ) . GetAttribute < FVector > ( VertexInstanceID , MeshAttribute : : VertexInstance : : Normal ) ;
Normal [ 0 ] = VertexNormal . X ;
Normal [ 1 ] = VertexNormal . Y ;
Normal [ 2 ] = VertexNormal . Z ;
}
void MikkSetTSpaceBasic ( const SMikkTSpaceContext * Context , const float Tangent [ 3 ] , const float BitangentSign , const int FaceIdx , const int VertIdx )
{
2018-07-16 11:43:36 -04:00
FMeshDescription * MeshDescription = ( FMeshDescription * ) ( Context - > m_pUserData ) ;
2018-03-21 10:54:37 -04:00
const FMeshPolygon & Polygon = MeshDescription - > GetPolygon ( FPolygonID ( FaceIdx ) ) ;
const FVertexInstanceID VertexInstanceID = Polygon . PerimeterContour . VertexInstanceIDs [ VertIdx ] ;
2018-02-22 10:04:16 -05:00
const FVector VertexTangent ( Tangent [ 0 ] , Tangent [ 1 ] , Tangent [ 2 ] ) ;
MeshDescription - > VertexInstanceAttributes ( ) . SetAttribute < FVector > ( VertexInstanceID , MeshAttribute : : VertexInstance : : Tangent , 0 , VertexTangent ) ;
MeshDescription - > VertexInstanceAttributes ( ) . SetAttribute < float > ( VertexInstanceID , MeshAttribute : : VertexInstance : : BinormalSign , 0 , - BitangentSign ) ;
}
void MikkGetTexCoord ( const SMikkTSpaceContext * Context , float UV [ 2 ] , const int FaceIdx , const int VertIdx )
{
2018-07-16 11:43:36 -04:00
FMeshDescription * MeshDescription = ( FMeshDescription * ) ( Context - > m_pUserData ) ;
2018-03-21 10:54:37 -04:00
const FMeshPolygon & Polygon = MeshDescription - > GetPolygon ( FPolygonID ( FaceIdx ) ) ;
const FVertexInstanceID VertexInstanceID = Polygon . PerimeterContour . VertexInstanceIDs [ VertIdx ] ;
2018-02-22 10:04:16 -05:00
const FVector2D & TexCoord = MeshDescription - > VertexInstanceAttributes ( ) . GetAttribute < FVector2D > ( VertexInstanceID , MeshAttribute : : VertexInstance : : TextureCoordinate , 0 ) ;
UV [ 0 ] = TexCoord . X ;
UV [ 1 ] = TexCoord . Y ;
}
}
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : FindOverlappingCorners ( TMultiMap < int32 , int32 > & OverlappingCorners , const FMeshDescription & MeshDescription , float ComparisonThreshold )
2018-02-22 11:20:51 -05:00
{
//Empty the old data
OverlappingCorners . Reset ( ) ;
2018-07-16 11:43:36 -04:00
const FVertexInstanceArray & VertexInstanceArray = MeshDescription . VertexInstances ( ) ;
const FVertexArray & VertexArray = MeshDescription . Vertices ( ) ;
2018-02-22 11:20:51 -05:00
const int32 NumWedges = VertexInstanceArray . Num ( ) ;
// Create a list of vertex Z/index pairs
2018-04-06 13:34:42 -04:00
TArray < MeshDescriptionOperationNamespace : : FIndexAndZ > VertIndexAndZ ;
2018-02-22 11:20:51 -05:00
VertIndexAndZ . Reserve ( NumWedges ) ;
2018-07-16 11:43:36 -04:00
const TVertexAttributeArray < FVector > & VertexPositions = MeshDescription . VertexAttributes ( ) . GetAttributes < FVector > ( MeshAttribute : : Vertex : : Position ) ;
2018-02-22 11:20:51 -05:00
for ( const FVertexInstanceID VertexInstanceID : VertexInstanceArray . GetElementIDs ( ) )
{
2018-07-16 11:43:36 -04:00
new ( VertIndexAndZ ) MeshDescriptionOperationNamespace : : FIndexAndZ ( VertexInstanceID . GetValue ( ) , VertexPositions [ MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) ] ) ;
2018-02-22 11:20:51 -05:00
}
// Sort the vertices by z value
2018-04-06 13:34:42 -04:00
VertIndexAndZ . Sort ( MeshDescriptionOperationNamespace : : FCompareIndexAndZ ( ) ) ;
2018-02-22 11:20:51 -05:00
// Search for duplicates, quickly!
for ( int32 i = 0 ; i < VertIndexAndZ . Num ( ) ; i + + )
{
// only need to search forward, since we add pairs both ways
for ( int32 j = i + 1 ; j < VertIndexAndZ . Num ( ) ; j + + )
{
if ( FMath : : Abs ( VertIndexAndZ [ j ] . Z - VertIndexAndZ [ i ] . Z ) > ComparisonThreshold )
break ; // can't be any more dups
const FVector & PositionA = * ( VertIndexAndZ [ i ] . OriginalVector ) ;
const FVector & PositionB = * ( VertIndexAndZ [ j ] . OriginalVector ) ;
if ( PositionA . Equals ( PositionB , ComparisonThreshold ) )
{
OverlappingCorners . Add ( VertIndexAndZ [ i ] . Index , VertIndexAndZ [ j ] . Index ) ;
OverlappingCorners . Add ( VertIndexAndZ [ j ] . Index , VertIndexAndZ [ i ] . Index ) ;
}
}
}
}
2018-07-16 11:43:36 -04:00
void FMeshDescriptionOperations : : CreateLightMapUVLayout ( FMeshDescription & MeshDescription ,
2018-02-22 11:20:51 -05:00
int32 SrcLightmapIndex ,
int32 DstLightmapIndex ,
int32 MinLightmapResolution ,
ELightmapUVVersion LightmapUVVersion ,
const TMultiMap < int32 , int32 > & OverlappingCorners )
{
2018-04-25 17:41:58 -04:00
MeshDescriptionOp : : FLayoutUV Packer ( MeshDescription , SrcLightmapIndex , DstLightmapIndex , MinLightmapResolution ) ;
2018-02-22 11:20:51 -05:00
Packer . SetVersion ( LightmapUVVersion ) ;
Packer . FindCharts ( OverlappingCorners ) ;
bool bPackSuccess = Packer . FindBestPacking ( ) ;
if ( bPackSuccess )
{
Packer . CommitPackedUVs ( ) ;
}
}
2018-07-16 11:43:36 -04:00
bool FMeshDescriptionOperations : : GenerateUniqueUVsForStaticMesh ( const FMeshDescription & MeshDescription , int32 TextureResolution , TArray < FVector2D > & OutTexCoords )
2018-02-22 11:20:51 -05:00
{
// Create a copy of original mesh (only copy necessary data)
2018-07-16 11:43:36 -04:00
FMeshDescription DuplicateMeshDescription ( MeshDescription ) ;
2018-02-22 11:20:51 -05:00
// Find overlapping corners for UV generator. Allow some threshold - this should not produce any error in a case if resulting
// mesh will not merge these vertices.
TMultiMap < int32 , int32 > OverlappingCorners ;
FindOverlappingCorners ( OverlappingCorners , DuplicateMeshDescription , THRESH_POINTS_ARE_SAME ) ;
// Generate new UVs
2018-04-25 17:41:58 -04:00
MeshDescriptionOp : : FLayoutUV Packer ( DuplicateMeshDescription , 0 , 1 , FMath : : Clamp ( TextureResolution / 4 , 32 , 512 ) ) ;
2018-02-22 11:20:51 -05:00
Packer . FindCharts ( OverlappingCorners ) ;
bool bPackSuccess = Packer . FindBestPacking ( ) ;
if ( bPackSuccess )
{
Packer . CommitPackedUVs ( ) ;
2018-07-16 11:43:36 -04:00
TVertexInstanceAttributeIndicesArray < FVector2D > & VertexInstanceUVs = DuplicateMeshDescription . VertexInstanceAttributes ( ) . GetAttributesSet < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-02-22 11:20:51 -05:00
// Save generated UVs
check ( VertexInstanceUVs . GetNumIndices ( ) > 1 ) ;
auto & UniqueUVsArray = VertexInstanceUVs . GetArrayForIndex ( 1 ) ;
OutTexCoords . AddZeroed ( UniqueUVsArray . Num ( ) ) ;
int32 TextureCoordIndex = 0 ;
2018-07-16 11:43:36 -04:00
for ( const FVertexInstanceID & VertexInstanceID : DuplicateMeshDescription . VertexInstances ( ) . GetElementIDs ( ) )
2018-02-22 11:20:51 -05:00
{
OutTexCoords [ TextureCoordIndex + + ] = UniqueUVsArray [ VertexInstanceID ] ;
}
}
return bPackSuccess ;
}
2018-08-16 13:53:43 -04:00
bool FMeshDescriptionOperations : : AddUVChannel ( FMeshDescription & MeshDescription )
{
TVertexInstanceAttributeIndicesArray < FVector2D > & VertexInstanceUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesSet < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
if ( VertexInstanceUVs . GetNumIndices ( ) > = MAX_MESH_TEXTURE_COORDS )
{
UE_LOG ( LogMeshDescriptionOperations , Error , TEXT ( " AddUVChannel: Cannot add UV channel. Maximum number of UV channels reached (%d). " ) , MAX_MESH_TEXTURE_COORDS ) ;
return false ;
}
VertexInstanceUVs . AddArray ( ) ;
return true ;
}
bool FMeshDescriptionOperations : : InsertUVChannel ( FMeshDescription & MeshDescription , int32 UVChannelIndex )
{
TVertexInstanceAttributeIndicesArray < FVector2D > & VertexInstanceUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesSet < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
if ( UVChannelIndex < 0 | | UVChannelIndex > VertexInstanceUVs . GetNumIndices ( ) )
{
UE_LOG ( LogMeshDescriptionOperations , Error , TEXT ( " InsertUVChannel: Cannot insert UV channel. Given UV channel index %d is out of bounds. " ) , UVChannelIndex ) ;
return false ;
}
if ( VertexInstanceUVs . GetNumIndices ( ) > = MAX_MESH_TEXTURE_COORDS )
{
UE_LOG ( LogMeshDescriptionOperations , Error , TEXT ( " InsertUVChannel: Cannot insert UV channel. Maximum number of UV channels reached (%d). " ) , MAX_MESH_TEXTURE_COORDS ) ;
return false ;
}
VertexInstanceUVs . InsertArray ( UVChannelIndex ) ;
return true ;
}
bool FMeshDescriptionOperations : : RemoveUVChannel ( FMeshDescription & MeshDescription , int32 UVChannelIndex )
{
TVertexInstanceAttributeIndicesArray < FVector2D > & VertexInstanceUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesSet < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
if ( VertexInstanceUVs . GetNumIndices ( ) = = 1 )
{
UE_LOG ( LogMeshDescriptionOperations , Error , TEXT ( " RemoveUVChannel: Cannot remove UV channel. There must be at least one channel. " ) ) ;
return false ;
}
if ( UVChannelIndex < 0 | | UVChannelIndex > = VertexInstanceUVs . GetNumIndices ( ) )
{
UE_LOG ( LogMeshDescriptionOperations , Error , TEXT ( " RemoveUVChannel: Cannot remove UV channel. Given UV channel index %d is out of bounds. " ) , UVChannelIndex ) ;
return false ;
}
VertexInstanceUVs . RemoveArray ( UVChannelIndex ) ;
return true ;
}
2018-02-22 10:04:16 -05:00
# undef LOCTEXT_NAMESPACE