2018-12-14 13:44:01 -05:00
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
2018-02-22 10:04:16 -05:00
# 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"
2018-09-04 14:32:41 -04:00
# include "LayoutUV.h"
# include "OverlappingCorners.h"
2018-02-22 10:04:16 -05:00
# include "RenderUtils.h"
# include "mikktspace.h"
2018-09-05 10:17:10 -04:00
# include "UVMapSettings.h"
2019-01-29 05:14:09 -05:00
# include "Modules/ModuleManager.h"
IMPLEMENT_MODULE ( FDefaultModuleImpl , MeshDescriptionOperations )
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
2019-01-02 15:37:07 -05:00
void FMeshDescriptionOperations : : ConvertHardEdgesToSmoothGroup ( const FMeshDescription & SourceMeshDescription , TArray < uint32 > & FaceSmoothingMasks )
2018-02-22 10:04:16 -05:00
{
TMap < FPolygonID , uint32 > PolygonSmoothGroup ;
2018-06-29 20:48:58 -04:00
PolygonSmoothGroup . Reserve ( SourceMeshDescription . Polygons ( ) . GetArraySize ( ) ) ;
2018-02-22 10:04:16 -05:00
TArray < bool > ConsumedPolygons ;
2018-06-29 20:48:58 -04:00
ConsumedPolygons . AddZeroed ( SourceMeshDescription . Polygons ( ) . GetArraySize ( ) ) ;
2018-02-22 10:04:16 -05:00
TMap < FPolygonID , uint32 > PolygonAvoidances ;
2018-09-04 14:32:41 -04:00
TEdgeAttributesConstRef < bool > EdgeHardnesses = SourceMeshDescription . EdgeAttributes ( ) . GetAttributesRef < bool > ( MeshAttribute : : Edge : : IsHard ) ;
2019-01-02 15:37:07 -05:00
int32 TriangleCount = 0 ;
2018-06-29 20:48:58 -04:00
for ( const FPolygonID PolygonID : SourceMeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
2019-01-02 15:37:07 -05:00
TriangleCount + = SourceMeshDescription . GetPolygonTriangles ( PolygonID ) . Num ( ) ;
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-06-29 20:48:58 -04:00
SourceMeshDescription . GetPolygonEdges ( CurrentPolygonID , PolygonEdges ) ;
2018-02-22 10:04:16 -05:00
for ( const FEdgeID & EdgeID : PolygonEdges )
{
bool bIsHardEdge = EdgeHardnesses [ EdgeID ] ;
2018-06-29 20:48:58 -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 ;
}
}
2019-01-02 15:37:07 -05:00
//Set the smooth group in the FaceSmoothingMasks parameter
check ( FaceSmoothingMasks . Num ( ) = = TriangleCount ) ;
2018-02-22 10:04:16 -05:00
int32 TriangleIndex = 0 ;
2018-06-29 20:48:58 -04:00
for ( const FPolygonID PolygonID : SourceMeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
uint32 PolygonSmoothValue = PolygonSmoothGroup [ PolygonID ] ;
2018-06-29 20:48:58 -04:00
const TArray < FMeshTriangle > & Triangles = SourceMeshDescription . GetPolygonTriangles ( PolygonID ) ;
2018-02-22 10:04:16 -05:00
for ( const FMeshTriangle & MeshTriangle : Triangles )
{
2019-01-02 15:37:07 -05:00
FaceSmoothingMasks [ TriangleIndex + + ] = PolygonSmoothValue ;
2018-02-22 10:04:16 -05:00
}
}
}
2018-09-04 14:32:41 -04:00
void FMeshDescriptionOperations : : ConvertSmoothGroupToHardEdges ( const TArray < uint32 > & FaceSmoothingMasks , FMeshDescription & DestinationMeshDescription )
2018-02-22 10:04:16 -05:00
{
2018-09-04 14:32:41 -04:00
TEdgeAttributesRef < bool > EdgeHardnesses = DestinationMeshDescription . EdgeAttributes ( ) . GetAttributesRef < bool > ( MeshAttribute : : Edge : : IsHard ) ;
2018-02-22 10:04:16 -05:00
TArray < bool > ConsumedPolygons ;
2018-06-29 20:48:58 -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 ( ) ;
2018-09-04 14:32:41 -04:00
check ( FaceSmoothingMasks . IsValidIndex ( CurrentPolygonIDValue ) ) ;
const uint32 ReferenceSmoothGroup = FaceSmoothingMasks [ CurrentPolygonIDValue ] ;
2018-02-22 10:04:16 -05:00
TArray < FEdgeID > PolygonEdges ;
2018-06-29 20:48:58 -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-06-29 20:48:58 -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 ;
}
2018-09-04 14:32:41 -04:00
check ( FaceSmoothingMasks . IsValidIndex ( EdgePolygonIDValue ) ) ;
const uint32 TestSmoothGroup = FaceSmoothingMasks [ EdgePolygonIDValue ] ;
2018-02-22 10:04:16 -05:00
if ( ( TestSmoothGroup & ReferenceSmoothGroup ) = = 0 )
{
EdgeHardnesses [ EdgeID ] = true ;
break ;
}
else
{
ConnectedPolygons . Add ( EdgePolygonID ) ;
}
}
}
ConsumedPolygons [ CurrentPolygonID . GetValue ( ) ] = true ;
}
}
}
2018-06-29 20:48:58 -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-09-04 14:32:41 -04:00
TVertexAttributesConstRef < FVector > VertexPositions = SourceMeshDescription . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
2018-02-22 10:04:16 -05:00
2018-09-04 14:32:41 -04:00
TVertexInstanceAttributesConstRef < FVector > VertexInstanceNormals = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Normal ) ;
TVertexInstanceAttributesConstRef < FVector > VertexInstanceTangents = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Tangent ) ;
TVertexInstanceAttributesConstRef < float > VertexInstanceBinormalSigns = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : VertexInstance : : BinormalSign ) ;
TVertexInstanceAttributesConstRef < FVector4 > VertexInstanceColors = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector4 > ( MeshAttribute : : VertexInstance : : Color ) ;
TVertexInstanceAttributesConstRef < FVector2D > VertexInstanceUVs = SourceMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-02-22 10:04:16 -05:00
2018-09-04 14:32:41 -04:00
TPolygonGroupAttributesConstRef < FName > PolygonGroupMaterialSlotName = SourceMeshDescription . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
2018-02-22 10:04:16 -05:00
2018-06-29 20:48:58 -04:00
DestinationRawMesh . VertexPositions . AddZeroed ( SourceMeshDescription . Vertices ( ) . Num ( ) ) ;
TArray < int32 > RemapVerts ;
RemapVerts . AddZeroed ( SourceMeshDescription . Vertices ( ) . GetArraySize ( ) ) ;
int32 VertexIndex = 0 ;
for ( const FVertexID & VertexID : SourceMeshDescription . Vertices ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
2018-06-29 20:48:58 -04:00
DestinationRawMesh . VertexPositions [ VertexIndex ] = VertexPositions [ VertexID ] ;
RemapVerts [ VertexID . GetValue ( ) ] = VertexIndex ;
+ + VertexIndex ;
2018-02-22 10:04:16 -05:00
}
int32 TriangleNumber = 0 ;
2018-06-29 20:48:58 -04:00
for ( const FPolygonID & PolygonID : SourceMeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 10:04:16 -05:00
{
2018-06-29 20:48:58 -04:00
TriangleNumber + = SourceMeshDescription . GetPolygonTriangles ( PolygonID ) . Num ( ) ;
2018-02-22 10:04:16 -05:00
}
DestinationRawMesh . FaceMaterialIndices . AddZeroed ( TriangleNumber ) ;
DestinationRawMesh . FaceSmoothingMasks . AddZeroed ( TriangleNumber ) ;
2019-01-02 15:37:07 -05:00
bool bHasVertexColor = HasVertexColor ( SourceMeshDescription ) ;
2018-06-29 20:48:58 -04:00
int32 WedgeIndexNumber = TriangleNumber * 3 ;
2019-01-02 15:37:07 -05:00
if ( bHasVertexColor )
{
DestinationRawMesh . WedgeColors . AddZeroed ( WedgeIndexNumber ) ;
}
2018-06-29 20:48:58 -04:00
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 )
2018-02-22 10:04:16 -05:00
{
2018-06-29 20:48:58 -04:00
DestinationRawMesh . WedgeTexCoords [ UVIndex ] . AddZeroed ( WedgeIndexNumber ) ;
}
int32 TriangleIndex = 0 ;
int32 WedgeIndex = 0 ;
for ( const FPolygonID PolygonID : SourceMeshDescription . Polygons ( ) . GetElementIDs ( ) )
{
const FPolygonGroupID & PolygonGroupID = SourceMeshDescription . GetPolygonPolygonGroup ( PolygonID ) ;
2018-02-22 10:04:16 -05:00
int32 PolygonIDValue = PolygonID . GetValue ( ) ;
2018-06-29 20:48:58 -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
{
2019-01-02 15:37:07 -05:00
DestinationRawMesh . FaceMaterialIndices [ TriangleIndex ] = PolygonGroupID . GetValue ( ) ;
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-29 20:48:58 -04:00
2019-01-02 15:37:07 -05:00
if ( bHasVertexColor )
{
DestinationRawMesh . WedgeColors [ WedgeIndex ] = FLinearColor ( VertexInstanceColors [ VertexInstanceID ] ) . ToFColor ( true ) ;
}
2018-06-29 20:48:58 -04:00
DestinationRawMesh . WedgeIndices [ WedgeIndex ] = RemapVerts [ SourceMeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) . GetValue ( ) ] ;
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-09-04 14:32:41 -04:00
DestinationRawMesh . WedgeTexCoords [ UVIndex ] [ WedgeIndex ] = VertexInstanceUVs . Get ( VertexInstanceID , UVIndex ) ;
2018-02-22 10:04:16 -05:00
}
2018-06-29 20:48:58 -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
2019-01-02 15:37:07 -05:00
ConvertHardEdgesToSmoothGroup ( SourceMeshDescription , DestinationRawMesh . FaceSmoothingMasks ) ;
2018-02-22 10:04:16 -05:00
}
2018-06-29 20:48:58 -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-06-29 20:48:58 -04:00
void FillMeshDescriptionVertexPositionNoDuplicate ( const TArray < FVector > & RawMeshVertexPositions , FMeshDescription & DestinationMeshDescription , TArray < FVertexID > & RemapVertexPosition )
2018-03-29 16:39:17 -04:00
{
2018-09-04 14:32:41 -04:00
TVertexAttributesRef < FVector > VertexPositions = DestinationMeshDescription . VertexAttributes ( ) . GetAttributesRef < 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-06-29 20:48:58 -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-06-29 20:48:58 -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-06-29 20:48:58 -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-06-29 20:48:58 -04:00
void FMeshDescriptionOperations : : ConvertFromRawMesh ( const FRawMesh & SourceRawMesh , FMeshDescription & DestinationMeshDescription , const TMap < int32 , FName > & MaterialMap )
2018-02-22 10:04:16 -05:00
{
2018-06-29 20:48:58 -04:00
DestinationMeshDescription . Empty ( ) ;
2018-02-22 10:04:16 -05:00
2018-06-29 20:48:58 -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-06-29 20:48:58 -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-09-04 14:32:41 -04:00
TVertexInstanceAttributesRef < FVector > VertexInstanceNormals = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Normal ) ;
TVertexInstanceAttributesRef < FVector > VertexInstanceTangents = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Tangent ) ;
TVertexInstanceAttributesRef < float > VertexInstanceBinormalSigns = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : VertexInstance : : BinormalSign ) ;
TVertexInstanceAttributesRef < FVector4 > VertexInstanceColors = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector4 > ( MeshAttribute : : VertexInstance : : Color ) ;
TVertexInstanceAttributesRef < FVector2D > VertexInstanceUVs = DestinationMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-02-22 10:04:16 -05:00
2018-09-04 14:32:41 -04:00
TPolygonGroupAttributesRef < FName > PolygonGroupImportedMaterialSlotNames = DestinationMeshDescription . PolygonGroupAttributes ( ) . GetAttributesRef < 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
{
2019-01-29 19:24:53 -05:00
FPolygonGroupID PolygonGroupID = DestinationMeshDescription . CreatePolygonGroup ( ) ;
2018-04-04 18:36:40 -04:00
if ( MaterialMap . Contains ( MaterialIndex ) )
{
PolygonGroupImportedMaterialSlotNames [ PolygonGroupID ] = MaterialMap [ MaterialIndex ] ;
}
2019-01-02 15:37:07 -05:00
else
{
2019-01-29 19:24:53 -05:00
PolygonGroupImportedMaterialSlotNames [ PolygonGroupID ] = FName ( * FString : : Printf ( TEXT ( " MaterialSlot_%d " ) , PolygonGroupID . GetValue ( ) ) ) ;
2019-01-02 15:37:07 -05:00
}
2018-04-04 18:36:40 -04:00
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 ;
}
2019-01-02 15:37:07 -05:00
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-06-29 20:48:58 -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
}
}
2019-01-02 15:37:07 -05:00
2018-02-22 10:04:16 -05:00
if ( PolygonGroupID = = FPolygonGroupID : : Invalid )
{
2018-06-29 20:48:58 -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
}
2019-01-29 19:24:53 -05:00
TArray < FVertexInstanceID > TriangleVertexInstanceIDs ;
TriangleVertexInstanceIDs . SetNum ( 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-06-29 20:48:58 -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 ;
}
2018-09-04 14:32:41 -04:00
VertexInstanceUVs . Set ( VertexInstanceID , TextureCoordIndex , SourceRawMesh . WedgeTexCoords [ TextureCoordinnateIndex ] [ VerticeIndex ] ) ;
2018-02-22 10:04:16 -05:00
}
}
2019-01-29 19:24:53 -05:00
const FPolygonID NewPolygonID = DestinationMeshDescription . CreatePolygon ( PolygonGroupID , TriangleVertexInstanceIDs ) ;
2018-06-29 20:48:58 -04:00
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 ) ;
}
}
2019-01-02 15:37:07 -05:00
2018-09-04 14:32:41 -04:00
ConvertSmoothGroupToHardEdges ( SourceRawMesh . FaceSmoothingMasks , 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-06-29 20:48:58 -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-06-29 20:48:58 -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
}
}
2019-01-02 15:37:07 -05:00
void FMeshDescriptionOperations : : AppendMeshDescription ( const FMeshDescription & SourceMesh , FMeshDescription & TargetMesh , FAppendSettings & AppendSettings )
{
//Vertex Attributes
TVertexAttributesConstRef < FVector > SourceVertexPositions = SourceMesh . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
TVertexAttributesConstRef < float > SourceVertexCornerSharpness = SourceMesh . VertexAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : Vertex : : CornerSharpness ) ;
TVertexAttributesRef < FVector > TargetVertexPositions = TargetMesh . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
TVertexAttributesRef < float > TargetVertexCornerSharpness = TargetMesh . VertexAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : Vertex : : CornerSharpness ) ;
//Edge Attributes
TEdgeAttributesConstRef < bool > SourceEdgeHardnesses = SourceMesh . EdgeAttributes ( ) . GetAttributesRef < bool > ( MeshAttribute : : Edge : : IsHard ) ;
TEdgeAttributesConstRef < float > SourceEdgeCreaseSharpnesses = SourceMesh . EdgeAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : Edge : : CreaseSharpness ) ;
TEdgeAttributesRef < bool > TargetEdgeHardnesses = TargetMesh . EdgeAttributes ( ) . GetAttributesRef < bool > ( MeshAttribute : : Edge : : IsHard ) ;
TEdgeAttributesRef < float > TargetEdgeCreaseSharpnesses = TargetMesh . EdgeAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : Edge : : CreaseSharpness ) ;
//PolygonGroup Attributes
TPolygonGroupAttributesConstRef < FName > SourceImportedMaterialSlotNames = SourceMesh . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
TPolygonGroupAttributesRef < FName > TargetImportedMaterialSlotNames = TargetMesh . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
//VertexInstance Attributes
TVertexInstanceAttributesConstRef < FVector > SourceVertexInstanceNormals = SourceMesh . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Normal ) ;
TVertexInstanceAttributesConstRef < FVector > SourceVertexInstanceTangents = SourceMesh . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Tangent ) ;
TVertexInstanceAttributesConstRef < float > SourceVertexInstanceBinormalSigns = SourceMesh . VertexInstanceAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : VertexInstance : : BinormalSign ) ;
TVertexInstanceAttributesConstRef < FVector4 > SourceVertexInstanceColors = SourceMesh . VertexInstanceAttributes ( ) . GetAttributesRef < FVector4 > ( MeshAttribute : : VertexInstance : : Color ) ;
TVertexInstanceAttributesConstRef < FVector2D > SourceVertexInstanceUVs = SourceMesh . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
TVertexInstanceAttributesRef < FVector > TargetVertexInstanceNormals = TargetMesh . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Normal ) ;
TVertexInstanceAttributesRef < FVector > TargetVertexInstanceTangents = TargetMesh . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Tangent ) ;
TVertexInstanceAttributesRef < float > TargetVertexInstanceBinormalSigns = TargetMesh . VertexInstanceAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : VertexInstance : : BinormalSign ) ;
TVertexInstanceAttributesRef < FVector4 > TargetVertexInstanceColors = TargetMesh . VertexInstanceAttributes ( ) . GetAttributesRef < FVector4 > ( MeshAttribute : : VertexInstance : : Color ) ;
TVertexInstanceAttributesRef < FVector2D > TargetVertexInstanceUVs = TargetMesh . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
//Copy into the target mesh
TargetMesh . ReserveNewVertices ( SourceMesh . Vertices ( ) . Num ( ) ) ;
TargetMesh . ReserveNewVertexInstances ( SourceMesh . VertexInstances ( ) . Num ( ) ) ;
TargetMesh . ReserveNewEdges ( SourceMesh . Edges ( ) . Num ( ) ) ;
TargetMesh . ReserveNewPolygons ( SourceMesh . Polygons ( ) . Num ( ) ) ;
if ( SourceVertexInstanceUVs . GetNumIndices ( ) > TargetVertexInstanceUVs . GetNumIndices ( ) )
{
TargetVertexInstanceUVs . SetNumIndices ( SourceVertexInstanceUVs . GetNumIndices ( ) ) ;
}
//PolygonGroups
PolygonGroupMap RemapPolygonGroup ;
if ( AppendSettings . PolygonGroupsDelegate . IsBound ( ) )
{
AppendSettings . PolygonGroupsDelegate . Execute ( SourceMesh , TargetMesh , RemapPolygonGroup ) ;
}
else
{
for ( FPolygonGroupID SourcePolygonGroupID : SourceMesh . PolygonGroups ( ) . GetElementIDs ( ) )
{
FPolygonGroupID TargetMatchingID = FPolygonGroupID : : Invalid ;
for ( FPolygonGroupID TargetPolygonGroupID : TargetMesh . PolygonGroups ( ) . GetElementIDs ( ) )
{
if ( SourceImportedMaterialSlotNames [ SourcePolygonGroupID ] = = TargetImportedMaterialSlotNames [ TargetPolygonGroupID ] )
{
TargetMatchingID = TargetPolygonGroupID ;
break ;
}
}
if ( TargetMatchingID = = FPolygonGroupID : : Invalid )
{
TargetMatchingID = TargetMesh . CreatePolygonGroup ( ) ;
TargetImportedMaterialSlotNames [ TargetMatchingID ] = SourceImportedMaterialSlotNames [ SourcePolygonGroupID ] ;
}
RemapPolygonGroup . Add ( SourcePolygonGroupID , TargetMatchingID ) ;
}
}
//Vertices
TMap < FVertexID , FVertexID > SourceVertexIDRemap ;
SourceVertexIDRemap . Reserve ( SourceMesh . Vertices ( ) . Num ( ) ) ;
for ( FVertexID SourceVertexID : SourceMesh . Vertices ( ) . GetElementIDs ( ) )
{
FVertexID TargetVertexID = TargetMesh . CreateVertex ( ) ;
TargetVertexPositions [ TargetVertexID ] = ( SourceVertexPositions [ SourceVertexID ] - AppendSettings . MergedAssetPivot ) ;
TargetVertexCornerSharpness [ TargetVertexID ] = SourceVertexCornerSharpness [ SourceVertexID ] ;
SourceVertexIDRemap . Add ( SourceVertexID , TargetVertexID ) ;
}
// Transform vertices properties
if ( AppendSettings . MeshTransform )
{
const FTransform & Transform = AppendSettings . MeshTransform . GetValue ( ) ;
for ( const TPair < FVertexID , FVertexID > & VertexIDPair : SourceVertexIDRemap )
{
FVector & Position = TargetVertexPositions [ VertexIDPair . Value ] ;
Position = Transform . TransformPosition ( Position ) ;
}
}
//Edges
TMap < FEdgeID , FEdgeID > SourceEdgeIDRemap ;
SourceEdgeIDRemap . Reserve ( SourceMesh . Edges ( ) . Num ( ) ) ;
for ( const FEdgeID & SourceEdgeID : SourceMesh . Edges ( ) . GetElementIDs ( ) )
{
const FMeshEdge & SourceEdge = SourceMesh . GetEdge ( SourceEdgeID ) ;
FEdgeID TargetEdgeID = TargetMesh . CreateEdge ( SourceVertexIDRemap [ SourceEdge . VertexIDs [ 0 ] ] , SourceVertexIDRemap [ SourceEdge . VertexIDs [ 1 ] ] ) ;
TargetEdgeHardnesses [ TargetEdgeID ] = SourceEdgeHardnesses [ SourceEdgeID ] ;
TargetEdgeCreaseSharpnesses [ TargetEdgeID ] = SourceEdgeCreaseSharpnesses [ SourceEdgeID ] ;
SourceEdgeIDRemap . Add ( SourceEdgeID , TargetEdgeID ) ;
}
//VertexInstances
TMap < FVertexInstanceID , FVertexInstanceID > SourceVertexInstanceIDRemap ;
SourceVertexInstanceIDRemap . Reserve ( SourceMesh . VertexInstances ( ) . Num ( ) ) ;
for ( const FVertexInstanceID & SourceVertexInstanceID : SourceMesh . VertexInstances ( ) . GetElementIDs ( ) )
{
FVertexInstanceID TargetVertexInstanceID = TargetMesh . CreateVertexInstance ( SourceVertexIDRemap [ SourceMesh . GetVertexInstanceVertex ( SourceVertexInstanceID ) ] ) ;
SourceVertexInstanceIDRemap . Add ( SourceVertexInstanceID , TargetVertexInstanceID ) ;
TargetVertexInstanceNormals [ TargetVertexInstanceID ] = SourceVertexInstanceNormals [ SourceVertexInstanceID ] ;
TargetVertexInstanceTangents [ TargetVertexInstanceID ] = SourceVertexInstanceTangents [ SourceVertexInstanceID ] ;
TargetVertexInstanceBinormalSigns [ TargetVertexInstanceID ] = SourceVertexInstanceBinormalSigns [ SourceVertexInstanceID ] ;
if ( AppendSettings . bMergeVertexColor )
{
TargetVertexInstanceColors [ TargetVertexInstanceID ] = SourceVertexInstanceColors [ SourceVertexInstanceID ] ;
}
for ( int32 UVChannelIndex = 0 ; UVChannelIndex < SourceVertexInstanceUVs . GetNumIndices ( ) ; + + UVChannelIndex )
{
TargetVertexInstanceUVs . Set ( TargetVertexInstanceID , UVChannelIndex , SourceVertexInstanceUVs . Get ( SourceVertexInstanceID , UVChannelIndex ) ) ;
}
}
// Transform vertex instances properties
if ( AppendSettings . MeshTransform )
{
const FTransform & Transform = AppendSettings . MeshTransform . GetValue ( ) ;
bool bFlipBinormal = Transform . GetDeterminant ( ) < 0 ;
float BinormalSignsFactor = bFlipBinormal ? - 1.f : 1.f ;
for ( const TPair < FVertexInstanceID , FVertexInstanceID > & VertexInstanceIDPair : SourceVertexInstanceIDRemap )
{
FVertexInstanceID InstanceID = VertexInstanceIDPair . Value ;
FVector & Normal = TargetVertexInstanceNormals [ InstanceID ] ;
Normal = Transform . TransformVectorNoScale ( Normal ) ;
FVector & Tangent = TargetVertexInstanceTangents [ InstanceID ] ;
Tangent = Transform . TransformVectorNoScale ( Tangent ) ;
TargetVertexInstanceBinormalSigns [ InstanceID ] * = BinormalSignsFactor ;
}
}
//Polygons
for ( const FPolygonID & SourcePolygonID : SourceMesh . Polygons ( ) . GetElementIDs ( ) )
{
const FMeshPolygon & SourcePolygon = SourceMesh . GetPolygon ( SourcePolygonID ) ;
//Find the polygonGroupID
FPolygonGroupID TargetPolygonGroupID = RemapPolygonGroup [ SourcePolygon . PolygonGroupID ] ;
int32 PolygonVertexCount = SourcePolygon . PerimeterContour . VertexInstanceIDs . Num ( ) ;
2019-01-29 19:24:53 -05:00
TArray < FVertexInstanceID > VertexInstanceIDs ;
VertexInstanceIDs . Reserve ( PolygonVertexCount ) ;
for ( const FVertexInstanceID VertexInstanceID : SourcePolygon . PerimeterContour . VertexInstanceIDs )
2019-01-02 15:37:07 -05:00
{
2019-01-29 19:24:53 -05:00
VertexInstanceIDs . Add ( SourceVertexInstanceIDRemap [ VertexInstanceID ] ) ;
2019-01-02 15:37:07 -05:00
}
// Insert a polygon into the mesh
2019-01-29 19:24:53 -05:00
const FPolygonID TargetPolygonID = TargetMesh . CreatePolygon ( TargetPolygonGroupID , VertexInstanceIDs ) ;
2019-01-02 15:37:07 -05:00
//Triangulate the polygon
FMeshPolygon & Polygon = TargetMesh . GetPolygon ( TargetPolygonID ) ;
TargetMesh . ComputePolygonTriangulation ( TargetPolygonID , Polygon . Triangles ) ;
}
}
2018-02-22 10:04:16 -05:00
//////////////////////////////////////////////////////////////////////////
// Normals tangents and Bi-normals
2019-01-02 15:37:07 -05:00
void FMeshDescriptionOperations : : RecomputeNormalsAndTangentsIfNeeded ( FMeshDescription & MeshDescription , ETangentOptions TangentOptions , bool bUseMikkTSpace , bool bForceRecomputeNormals , bool bForceRecomputeTangents )
{
bool bRecomputeNormals = bForceRecomputeNormals ;
bool bRecomputeTangents = bForceRecomputeTangents ;
TVertexInstanceAttributesRef < FVector > VertexInstanceNormals = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Normal ) ;
TVertexInstanceAttributesRef < FVector > VertexInstanceTangents = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Tangent ) ;
if ( ! bRecomputeNormals | | ! bRecomputeTangents )
{
for ( const FVertexInstanceID & VertexInstanceID : MeshDescription . VertexInstances ( ) . GetElementIDs ( ) )
{
bRecomputeNormals | = ( VertexInstanceNormals [ VertexInstanceID ] . IsNearlyZero ( ) | | VertexInstanceNormals [ VertexInstanceID ] . ContainsNaN ( ) ) ;
bRecomputeTangents | = ( VertexInstanceTangents [ VertexInstanceID ] . IsNearlyZero ( ) | | VertexInstanceTangents [ VertexInstanceID ] . ContainsNaN ( ) ) ;
if ( bRecomputeNormals & & bRecomputeTangents )
{
break ;
}
}
}
if ( bRecomputeNormals | | bRecomputeTangents )
{
//Zero out all value that need to be recompute
for ( const FVertexInstanceID & VertexInstanceID : MeshDescription . VertexInstances ( ) . GetElementIDs ( ) )
{
if ( bRecomputeNormals )
{
VertexInstanceNormals [ VertexInstanceID ] = FVector : : ZeroVector ;
}
if ( bRecomputeTangents )
{
VertexInstanceTangents [ VertexInstanceID ] = FVector : : ZeroVector ;
}
}
if ( bRecomputeNormals )
{
FMeshDescriptionOperations : : CreateNormals ( MeshDescription , TangentOptions , bUseMikkTSpace ? false : bRecomputeTangents ) ;
}
if ( bUseMikkTSpace & & bRecomputeTangents )
{
FMeshDescriptionOperations : : CreateMikktTangents ( MeshDescription , TangentOptions ) ;
}
}
}
2018-02-22 10:04:16 -05:00
2018-06-29 20:48:58 -04:00
void FMeshDescriptionOperations : : CreatePolygonNTB ( FMeshDescription & MeshDescription , float ComparisonThreshold )
2018-02-22 10:04:16 -05:00
{
2019-01-02 15:37:07 -05:00
MeshDescription . PolygonAttributes ( ) . RegisterAttribute < FVector > ( MeshAttribute : : Polygon : : Normal , 1 , FVector : : ZeroVector , EMeshAttributeFlags : : Transient ) ;
MeshDescription . PolygonAttributes ( ) . RegisterAttribute < FVector > ( MeshAttribute : : Polygon : : Tangent , 1 , FVector : : ZeroVector , EMeshAttributeFlags : : Transient ) ;
MeshDescription . PolygonAttributes ( ) . RegisterAttribute < FVector > ( MeshAttribute : : Polygon : : Binormal , 1 , FVector : : ZeroVector , EMeshAttributeFlags : : Transient ) ;
MeshDescription . PolygonAttributes ( ) . RegisterAttribute < FVector > ( MeshAttribute : : Polygon : : Center , 1 , FVector : : ZeroVector , EMeshAttributeFlags : : Transient ) ;
TVertexAttributesConstRef < FVector > VertexPositions = MeshDescription . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
TVertexInstanceAttributesConstRef < FVector2D > VertexUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-09-04 14:32:41 -04:00
TPolygonAttributesRef < FVector > PolygonNormals = MeshDescription . PolygonAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Polygon : : Normal ) ;
TPolygonAttributesRef < FVector > PolygonTangents = MeshDescription . PolygonAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Polygon : : Tangent ) ;
TPolygonAttributesRef < FVector > PolygonBinormals = MeshDescription . PolygonAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Polygon : : Binormal ) ;
2019-01-02 15:37:07 -05:00
TPolygonAttributesRef < FVector > PolygonCenters = MeshDescription . PolygonAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Polygon : : Center ) ;
2018-02-22 10:04:16 -05:00
2018-06-29 20:48:58 -04:00
FVertexInstanceArray & VertexInstanceArray = MeshDescription . VertexInstances ( ) ;
FVertexArray & VertexArray = MeshDescription . Vertices ( ) ;
FPolygonArray & PolygonArray = MeshDescription . Polygons ( ) ;
2018-02-22 10:04:16 -05:00
2018-06-29 20:48:58 -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-06-29 20:48:58 -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 ) ;
2018-09-04 14:32:41 -04:00
UVs [ i ] = VertexUVs . Get ( VertexInstanceID , 0 ) ; // UV0
2018-06-29 20:48:58 -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 ) ;
2019-01-02 15:37:07 -05:00
2018-03-21 10:54:37 -04:00
//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 ;
2019-01-02 15:37:07 -05:00
// Calculate polygon center: just an average of all vertex positions.
FVector Center = FVector : : ZeroVector ;
const TArray < FVertexInstanceID > & VertexInstanceIDs = MeshDescription . GetPolygonPerimeterVertexInstances ( PolygonID ) ;
for ( const FVertexInstanceID VertexInstanceID : VertexInstanceIDs )
{
Center + = VertexPositions [ MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) ] ;
}
Center / = float ( VertexInstanceIDs . Num ( ) ) ;
PolygonCenters [ PolygonID ] = Center ;
2018-02-22 10:04:16 -05:00
}
}
2019-01-02 15:37:07 -05:00
struct FNTBGroupKeyFuncs : public TDefaultMapKeyFuncs < FVector2D , FVector , false >
{
//We need to sanitize the key here to make sure -0.0f fall on the same hash then 0.0f
static FORCEINLINE_DEBUGGABLE uint32 GetKeyHash ( KeyInitType Key )
{
FVector2D TmpKey ;
TmpKey . X = FMath : : IsNearlyZero ( Key . X ) ? 0.0f : Key . X ;
TmpKey . Y = FMath : : IsNearlyZero ( Key . Y ) ? 0.0f : Key . Y ;
return FCrc : : MemCrc32 ( & TmpKey , sizeof ( TmpKey ) ) ;
}
} ;
2018-06-29 20:48:58 -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
/ / / / | \
2019-01-02 15:37:07 -05:00
// 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
2018-02-22 10:04:16 -05:00
2019-01-02 15:37:07 -05:00
// @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-02-22 10:04:16 -05:00
2019-01-02 15:37:07 -05:00
TVertexInstanceAttributesConstRef < FVector2D > VertexUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-09-04 14:32:41 -04:00
TVertexInstanceAttributesRef < FVector > VertexNormals = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Normal ) ;
TVertexInstanceAttributesRef < FVector > VertexTangents = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Tangent ) ;
TVertexInstanceAttributesRef < float > VertexBinormalSigns = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < float > ( MeshAttribute : : VertexInstance : : BinormalSign ) ;
2018-02-22 10:04:16 -05:00
2019-01-02 15:37:07 -05:00
TPolygonAttributesConstRef < FVector > PolygonNormals = MeshDescription . PolygonAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Polygon : : Normal ) ;
TPolygonAttributesConstRef < FVector > PolygonTangents = MeshDescription . PolygonAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Polygon : : Tangent ) ;
TPolygonAttributesConstRef < FVector > PolygonBinormals = MeshDescription . PolygonAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Polygon : : Binormal ) ;
check ( PolygonNormals . IsValid ( ) ) ;
check ( PolygonTangents . IsValid ( ) ) ;
check ( PolygonBinormals . IsValid ( ) ) ;
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-06-29 20:48:58 -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-06-29 20:48:58 -04:00
for ( const FEdgeID EdgeID : MeshDescription . GetVertexConnectedEdges ( VertexID ) )
2018-02-22 10:04:16 -05:00
{
2018-06-29 20:48:58 -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-06-29 20:48:58 -04:00
for ( const FVertexInstanceID VertexInstanceID : MeshDescription . GetPolygonPerimeterVertexInstances ( PolygonID ) )
2018-02-22 10:04:16 -05:00
{
2018-06-29 20:48:58 -04:00
if ( MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) = = VertexID )
2018-02-22 10:04:16 -05:00
{
VertexInfo . VertexInstanceID = VertexInstanceID ;
2018-09-04 14:32:41 -04:00
VertexInfo . UVs = VertexUVs . Get ( VertexInstanceID , 0 ) ; // UV0
2018-02-22 10:04:16 -05:00
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-09-04 14:32:41 -04:00
const TEdgeAttributesRef < bool > EdgeHardnesses = MeshDescription . EdgeAttributes ( ) . GetAttributesRef < 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-06-29 20:48:58 -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
2019-01-02 15:37:07 -05:00
TMap < FVector2D , FVector , FDefaultSetAllocator , FNTBGroupKeyFuncs > GroupTangent ;
TMap < FVector2D , FVector , FDefaultSetAllocator , FNTBGroupKeyFuncs > GroupBiNormal ;
2018-02-22 10:04:16 -05:00
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 ] ;
2019-01-02 15:37:07 -05:00
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 )
{
2018-09-04 14:32:41 -04:00
const FVector2D VertexUV = VertexUVs . Get ( VertexInstanceID , 0 ) ; // UV0
2018-02-22 10:04:16 -05:00
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-06-29 20:48:58 -04:00
void FMeshDescriptionOperations : : CreateMikktTangents ( FMeshDescription & MeshDescription , FMeshDescriptionOperations : : ETangentOptions TangentOptions )
2018-02-22 10:04:16 -05:00
{
2018-10-29 10:01:28 -04:00
// The Mikkt interface does not handle properly polygon array with 'holes'
// Compact mesh description if this is the case
if ( MeshDescription . Polygons ( ) . Num ( ) ! = MeshDescription . Polygons ( ) . GetArraySize ( ) )
{
FElementIDRemappings Remappings ;
MeshDescription . Compact ( Remappings ) ;
}
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-06-29 20:48:58 -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-14 15:58:25 -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-14 15:58:25 -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-06-29 20:48:58 -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-06-29 20:48:58 -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-06-29 20:48:58 -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-06-29 20:48:58 -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-09-04 14:32:41 -04:00
void FMeshDescriptionOperations : : FindOverlappingCorners ( FOverlappingCorners & OutOverlappingCorners , const FMeshDescription & MeshDescription , float ComparisonThreshold )
2018-02-22 11:20:51 -05:00
{
2018-09-04 14:32:41 -04:00
// @todo: this should be shared with FOverlappingCorners
2018-02-22 11:20:51 -05:00
2018-06-29 20:48:58 -04:00
const FVertexInstanceArray & VertexInstanceArray = MeshDescription . VertexInstances ( ) ;
const FVertexArray & VertexArray = MeshDescription . Vertices ( ) ;
2018-02-22 11:20:51 -05:00
2018-10-29 10:01:28 -04:00
int32 NumWedges = 0 ;
for ( const FPolygonID PolygonID : MeshDescription . Polygons ( ) . GetElementIDs ( ) )
{
2019-01-02 15:37:07 -05:00
NumWedges + = 3 * MeshDescription . GetPolygonTriangles ( PolygonID ) . Num ( ) ;
2018-10-29 10:01:28 -04:00
}
2018-02-22 11:20:51 -05:00
2018-09-04 14:32:41 -04:00
// Empty the old data and reserve space for new
OutOverlappingCorners . Init ( NumWedges ) ;
2018-02-22 11:20:51 -05:00
// 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-09-04 14:32:41 -04:00
TVertexAttributesConstRef < FVector > VertexPositions = MeshDescription . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
2018-02-22 11:20:51 -05:00
2018-10-29 10:01:28 -04:00
int32 WedgeIndex = 0 ;
for ( const FPolygonID PolygonID : MeshDescription . Polygons ( ) . GetElementIDs ( ) )
2018-02-22 11:20:51 -05:00
{
2018-10-29 10:01:28 -04:00
const TArray < FMeshTriangle > & Triangles = MeshDescription . GetPolygonTriangles ( PolygonID ) ;
2019-01-02 15:37:07 -05:00
for ( const FMeshTriangle & MeshTriangle : Triangles )
2018-10-29 10:01:28 -04:00
{
for ( int32 Corner = 0 ; Corner < 3 ; + + Corner )
{
const FVertexInstanceID VertexInstanceID = MeshTriangle . GetVertexInstanceID ( Corner ) ;
new ( VertIndexAndZ ) MeshDescriptionOperationNamespace : : FIndexAndZ ( WedgeIndex , VertexPositions [ MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) ] ) ;
+ + WedgeIndex ;
}
}
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 ) )
{
2018-09-04 14:32:41 -04:00
OutOverlappingCorners . Add ( VertIndexAndZ [ i ] . Index , VertIndexAndZ [ j ] . Index ) ;
OutOverlappingCorners . Add ( VertIndexAndZ [ j ] . Index , VertIndexAndZ [ i ] . Index ) ;
2018-02-22 11:20:51 -05:00
}
}
}
2018-09-04 14:32:41 -04:00
OutOverlappingCorners . FinishAdding ( ) ;
2018-02-22 11:20:51 -05:00
}
2018-09-04 14:32:41 -04:00
struct FLayoutUVMeshDescriptionView final : FLayoutUV : : IMeshView
{
FMeshDescription & MeshDescription ;
TVertexAttributesConstRef < FVector > Positions ;
TVertexInstanceAttributesConstRef < FVector > Normals ;
TVertexInstanceAttributesRef < FVector2D > TexCoords ;
const uint32 SrcChannel ;
const uint32 DstChannel ;
uint32 NumIndices = 0 ;
TArray < int32 > RemapVerts ;
TArray < FVector2D > FlattenedTexCoords ;
2019-01-02 15:37:07 -05:00
FLayoutUVMeshDescriptionView ( FMeshDescription & InMeshDescription , uint32 InSrcChannel , uint32 InDstChannel )
2018-09-04 14:32:41 -04:00
: MeshDescription ( InMeshDescription )
, Positions ( InMeshDescription . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) )
, Normals ( InMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : VertexInstance : : Normal ) )
, TexCoords ( InMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) )
, SrcChannel ( InSrcChannel )
, DstChannel ( InDstChannel )
{
uint32 NumTris = 0 ;
for ( const FPolygonID PolygonID : MeshDescription . Polygons ( ) . GetElementIDs ( ) )
{
NumTris + = MeshDescription . GetPolygonTriangles ( PolygonID ) . Num ( ) ;
}
NumIndices = NumTris * 3 ;
FlattenedTexCoords . SetNumUninitialized ( NumIndices ) ;
RemapVerts . SetNumUninitialized ( NumIndices ) ;
int32 WedgeIndex = 0 ;
for ( const FPolygonID PolygonID : MeshDescription . Polygons ( ) . GetElementIDs ( ) )
{
const TArray < FMeshTriangle > & Triangles = MeshDescription . GetPolygonTriangles ( PolygonID ) ;
for ( const FMeshTriangle MeshTriangle : Triangles )
{
for ( int32 Corner = 0 ; Corner < 3 ; + + Corner )
{
const FVertexInstanceID VertexInstanceID = MeshTriangle . GetVertexInstanceID ( Corner ) ;
FlattenedTexCoords [ WedgeIndex ] = TexCoords . Get ( VertexInstanceID , SrcChannel ) ;
RemapVerts [ WedgeIndex ] = VertexInstanceID . GetValue ( ) ;
+ + WedgeIndex ;
}
}
}
}
uint32 GetNumIndices ( ) const override { return NumIndices ; }
FVector GetPosition ( uint32 Index ) const override
2019-01-02 15:37:07 -05:00
{
2018-09-04 14:32:41 -04:00
FVertexInstanceID VertexInstanceID ( RemapVerts [ Index ] ) ;
FVertexID VertexID = MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) ;
return Positions [ VertexID ] ;
}
FVector GetNormal ( uint32 Index ) const override
2019-01-02 15:37:07 -05:00
{
2018-09-04 14:32:41 -04:00
FVertexInstanceID VertexInstanceID ( RemapVerts [ Index ] ) ;
return Normals [ VertexInstanceID ] ;
}
FVector2D GetInputTexcoord ( uint32 Index ) const override
{
return FlattenedTexCoords [ Index ] ;
}
void InitOutputTexcoords ( uint32 Num ) override
{
// If current DstChannel is out of range of the number of UVs defined by the mesh description, change the index count accordingly
const uint32 NumUVs = TexCoords . GetNumIndices ( ) ;
if ( DstChannel > = NumUVs )
{
TexCoords . SetNumIndices ( DstChannel + 1 ) ;
ensure ( false ) ; // not expecting it to get here
}
}
void SetOutputTexcoord ( uint32 Index , const FVector2D & Value ) override
{
const FVertexInstanceID VertexInstanceID ( RemapVerts [ Index ] ) ;
TexCoords . Set ( VertexInstanceID , DstChannel , Value ) ;
}
} ;
2018-06-29 20:48:58 -04:00
void FMeshDescriptionOperations : : CreateLightMapUVLayout ( FMeshDescription & MeshDescription ,
2018-02-22 11:20:51 -05:00
int32 SrcLightmapIndex ,
int32 DstLightmapIndex ,
int32 MinLightmapResolution ,
ELightmapUVVersion LightmapUVVersion ,
2018-09-04 14:32:41 -04:00
const FOverlappingCorners & OverlappingCorners )
2018-02-22 11:20:51 -05:00
{
2018-09-04 14:32:41 -04:00
FLayoutUVMeshDescriptionView MeshDescriptionView ( MeshDescription , SrcLightmapIndex , DstLightmapIndex ) ;
FLayoutUV Packer ( MeshDescriptionView , MinLightmapResolution ) ;
2018-02-22 11:20:51 -05:00
Packer . SetVersion ( LightmapUVVersion ) ;
Packer . FindCharts ( OverlappingCorners ) ;
bool bPackSuccess = Packer . FindBestPacking ( ) ;
if ( bPackSuccess )
{
Packer . CommitPackedUVs ( ) ;
}
}
2019-01-02 15:37:07 -05:00
bool FMeshDescriptionOperations : : GenerateUniqueUVsForStaticMesh ( const FMeshDescription & MeshDescription , int32 TextureResolution , bool bMergeIdenticalMaterials , TArray < FVector2D > & OutTexCoords )
2018-02-22 11:20:51 -05:00
{
// Create a copy of original mesh (only copy necessary data)
2018-06-29 20:48:58 -04:00
FMeshDescription DuplicateMeshDescription ( MeshDescription ) ;
2019-01-02 15:37:07 -05:00
TMap < FVertexInstanceID , FVertexInstanceID > RemapVertexInstance ;
//Remove the identical material
if ( bMergeIdenticalMaterials )
{
TVertexInstanceAttributesConstRef < FVector2D > VertexInstanceUVs = DuplicateMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
TArray < FPolygonID > ToDeletePolygons ;
RemapVertexInstance . Reserve ( DuplicateMeshDescription . VertexInstances ( ) . Num ( ) ) ;
TArray < FPolygonID > UniquePolygons ;
for ( FPolygonID RefPolygonID : DuplicateMeshDescription . Polygons ( ) . GetElementIDs ( ) )
{
FPolygonGroupID RefPolygonGroupID = DuplicateMeshDescription . GetPolygonPolygonGroup ( RefPolygonID ) ;
const TArray < FVertexInstanceID > & RefVertexInstances = DuplicateMeshDescription . GetPolygonPerimeterVertexInstances ( RefPolygonID ) ;
TArray < FVector2D > RefUVs ;
for ( FVertexInstanceID RefVertexInstanceID : RefVertexInstances )
{
RefUVs . Add ( VertexInstanceUVs [ RefVertexInstanceID ] ) ;
}
FPolygonID MatchPolygonID = FPolygonID : : Invalid ;
for ( FPolygonID TestPolygonID : UniquePolygons )
{
FPolygonGroupID TestPolygonGroupID = DuplicateMeshDescription . GetPolygonPolygonGroup ( TestPolygonID ) ;
if ( TestPolygonGroupID ! = RefPolygonGroupID )
{
continue ;
}
const TArray < FVertexInstanceID > & TestVertexInstances = DuplicateMeshDescription . GetPolygonPerimeterVertexInstances ( TestPolygonID ) ;
if ( TestVertexInstances . Num ( ) ! = RefVertexInstances . Num ( ) )
{
continue ;
}
bool bIdentical = true ;
int32 UVIndex = 0 ;
for ( FVertexInstanceID TestVertexInstanceID : TestVertexInstances )
{
if ( VertexInstanceUVs [ TestVertexInstanceID ] ! = RefUVs [ UVIndex ] )
{
bIdentical = false ;
break ;
}
UVIndex + + ;
}
if ( bIdentical )
{
MatchPolygonID = TestPolygonID ;
break ;
}
}
if ( MatchPolygonID = = FPolygonID : : Invalid )
{
UniquePolygons . Add ( RefPolygonID ) ;
for ( FVertexInstanceID RefVertexInstanceID : RefVertexInstances )
{
RemapVertexInstance . Add ( RefVertexInstanceID , RefVertexInstanceID ) ;
}
}
else
{
const TArray < FVertexInstanceID > & TestVertexInstances = DuplicateMeshDescription . GetPolygonPerimeterVertexInstances ( MatchPolygonID ) ;
int32 VertexInstanceIndex = 0 ;
for ( FVertexInstanceID RefVertexInstanceID : RefVertexInstances )
{
RemapVertexInstance . Add ( RefVertexInstanceID , TestVertexInstances [ VertexInstanceIndex ] ) ;
VertexInstanceIndex + + ;
}
ToDeletePolygons . Add ( RefPolygonID ) ;
}
}
//Delete polygons
if ( ToDeletePolygons . Num ( ) > 0 )
{
TArray < FEdgeID > OrphanedEdges ;
TArray < FVertexInstanceID > OrphanedVertexInstances ;
TArray < FPolygonGroupID > OrphanedPolygonGroups ;
TArray < FVertexID > OrphanedVertices ;
for ( FPolygonID PolygonID : ToDeletePolygons )
{
DuplicateMeshDescription . DeletePolygon ( PolygonID , & OrphanedEdges , & OrphanedVertexInstances , & OrphanedPolygonGroups ) ;
}
for ( FPolygonGroupID PolygonGroupID : OrphanedPolygonGroups )
{
DuplicateMeshDescription . DeletePolygonGroup ( PolygonGroupID ) ;
}
for ( FVertexInstanceID VertexInstanceID : OrphanedVertexInstances )
{
DuplicateMeshDescription . DeleteVertexInstance ( VertexInstanceID , & OrphanedVertices ) ;
}
for ( FEdgeID EdgeID : OrphanedEdges )
{
DuplicateMeshDescription . DeleteEdge ( EdgeID , & OrphanedVertices ) ;
}
for ( FVertexID VertexID : OrphanedVertices )
{
DuplicateMeshDescription . DeleteVertex ( VertexID ) ;
}
//Compact and Remap IDs so we have clean ID from 0 to n since we just erase some polygons
FElementIDRemappings RemappingInfos ;
DuplicateMeshDescription . Compact ( RemappingInfos ) ;
}
}
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.
2018-09-04 14:32:41 -04:00
FOverlappingCorners OverlappingCorners ;
2018-02-22 11:20:51 -05:00
FindOverlappingCorners ( OverlappingCorners , DuplicateMeshDescription , THRESH_POINTS_ARE_SAME ) ;
// Generate new UVs
2018-09-04 14:32:41 -04:00
FLayoutUVMeshDescriptionView DuplicateMeshDescriptionView ( DuplicateMeshDescription , 0 , 1 ) ;
FLayoutUV Packer ( DuplicateMeshDescriptionView , 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 ( ) ;
2019-01-02 15:37:07 -05:00
TVertexInstanceAttributesConstRef < FVector2D > DupVertexInstanceUVs = DuplicateMeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
TVertexInstanceAttributesConstRef < FVector2D > VertexInstanceUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-02-22 11:20:51 -05:00
// Save generated UVs
check ( VertexInstanceUVs . GetNumIndices ( ) > 1 ) ;
2018-09-04 14:32:41 -04:00
OutTexCoords . AddZeroed ( VertexInstanceUVs . GetNumElements ( ) ) ;
2018-02-22 11:20:51 -05:00
int32 TextureCoordIndex = 0 ;
2019-01-02 15:37:07 -05:00
for ( const FVertexInstanceID & VertexInstanceID : MeshDescription . VertexInstances ( ) . GetElementIDs ( ) )
2018-02-22 11:20:51 -05:00
{
2019-01-02 15:37:07 -05:00
FVertexInstanceID RemapID = bMergeIdenticalMaterials ? RemapVertexInstance [ VertexInstanceID ] : VertexInstanceID ;
// Save generated UVs
OutTexCoords [ TextureCoordIndex ] = VertexInstanceUVs . Get ( RemapID , 1 ) ; // UV1
TextureCoordIndex + + ;
2018-02-22 11:20:51 -05:00
}
}
return bPackSuccess ;
}
2018-08-14 15:58:25 -04:00
bool FMeshDescriptionOperations : : AddUVChannel ( FMeshDescription & MeshDescription )
{
2018-09-04 14:32:41 -04:00
TVertexInstanceAttributesRef < FVector2D > VertexInstanceUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-08-14 15:58:25 -04:00
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 ;
}
2018-09-04 14:32:41 -04:00
VertexInstanceUVs . SetNumIndices ( VertexInstanceUVs . GetNumIndices ( ) + 1 ) ;
2018-08-14 15:58:25 -04:00
return true ;
}
bool FMeshDescriptionOperations : : InsertUVChannel ( FMeshDescription & MeshDescription , int32 UVChannelIndex )
{
2018-09-04 14:32:41 -04:00
TVertexInstanceAttributesRef < FVector2D > VertexInstanceUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-08-14 15:58:25 -04:00
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 ;
}
2018-09-04 14:32:41 -04:00
VertexInstanceUVs . InsertIndex ( UVChannelIndex ) ;
2018-08-14 15:58:25 -04:00
return true ;
}
bool FMeshDescriptionOperations : : RemoveUVChannel ( FMeshDescription & MeshDescription , int32 UVChannelIndex )
{
2018-09-04 14:32:41 -04:00
TVertexInstanceAttributesRef < FVector2D > VertexInstanceUVs = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2018-08-14 15:58:25 -04:00
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 ;
}
2018-09-04 14:32:41 -04:00
VertexInstanceUVs . RemoveIndex ( UVChannelIndex ) ;
2018-08-14 15:58:25 -04:00
return true ;
}
2018-10-29 10:01:28 -04:00
void FMeshDescriptionOperations : : GeneratePlanarUV ( const FMeshDescription & MeshDescription , const FUVMapParameters & Params , TArray < FVector2D > & OutTexCoords )
2018-09-05 10:17:10 -04:00
{
2018-10-29 10:01:28 -04:00
// Project along X-axis (left view), UV along Z Y axes
FVector U = FVector : : UpVector ;
2018-09-05 10:17:10 -04:00
FVector V = FVector : : RightVector ;
TMeshAttributesConstRef < FVertexID , FVector > VertexPositions = MeshDescription . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
OutTexCoords . AddZeroed ( MeshDescription . VertexInstances ( ) . Num ( ) ) ;
2018-10-29 10:01:28 -04:00
FVector Size = Params . Size * Params . Scale ;
FVector Offset = Params . Position - Size / 2.f ;
2018-09-05 10:17:10 -04:00
int32 TextureCoordIndex = 0 ;
for ( const FVertexInstanceID & VertexInstanceID : MeshDescription . VertexInstances ( ) . GetElementIDs ( ) )
{
const FVertexID VertexID = MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) ;
FVector Vertex = VertexPositions [ VertexID ] ;
// Apply the gizmo transforms
2018-10-29 10:01:28 -04:00
Vertex = Params . Rotation . RotateVector ( Vertex ) ;
2018-09-05 10:17:10 -04:00
Vertex - = Offset ;
Vertex / = Size ;
2018-10-29 10:01:28 -04:00
float UCoord = FVector : : DotProduct ( Vertex , U ) * Params . UVTile . X ;
float VCoord = FVector : : DotProduct ( Vertex , V ) * Params . UVTile . Y ;
2018-09-05 10:17:10 -04:00
OutTexCoords [ TextureCoordIndex + + ] = FVector2D ( UCoord , VCoord ) ;
}
}
2018-10-29 10:01:28 -04:00
void FMeshDescriptionOperations : : GenerateCylindricalUV ( FMeshDescription & MeshDescription , const FUVMapParameters & Params , TArray < FVector2D > & OutTexCoords )
2018-09-05 10:17:10 -04:00
{
2018-10-29 10:01:28 -04:00
FVector Size = Params . Size * Params . Scale ;
FVector Offset = Params . Position ;
2018-09-05 10:17:10 -04:00
2018-10-29 10:01:28 -04:00
// Cylinder along X-axis, counterclockwise from -Y axis as seen from left view
FVector V = FVector : : ForwardVector ;
Offset . X - = Size . X / 2.f ;
2018-09-05 10:17:10 -04:00
TMeshAttributesConstRef < FVertexID , FVector > VertexPositions = MeshDescription . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
OutTexCoords . AddZeroed ( MeshDescription . VertexInstances ( ) . Num ( ) ) ;
const float AngleOffset = PI ; // offset to get the same result as in 3dsmax
int32 TextureCoordIndex = 0 ;
for ( const FVertexInstanceID & VertexInstanceID : MeshDescription . VertexInstances ( ) . GetElementIDs ( ) )
{
const FVertexID VertexID = MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) ;
FVector Vertex = VertexPositions [ VertexID ] ;
// Apply the gizmo transforms
2018-10-29 10:01:28 -04:00
Vertex = Params . Rotation . RotateVector ( Vertex ) ;
2018-09-05 10:17:10 -04:00
Vertex - = Offset ;
Vertex / = Size ;
2018-10-29 10:01:28 -04:00
float Angle = FMath : : Atan2 ( Vertex . Z , Vertex . Y ) ;
2018-09-05 10:17:10 -04:00
Angle + = AngleOffset ;
2018-10-29 10:01:28 -04:00
Angle * = Params . UVTile . X ;
2018-09-05 10:17:10 -04:00
float UCoord = Angle / ( 2 * PI ) ;
2018-10-29 10:01:28 -04:00
float VCoord = FVector : : DotProduct ( Vertex , V ) * Params . UVTile . Y ;
2018-09-05 10:17:10 -04:00
OutTexCoords [ TextureCoordIndex + + ] = FVector2D ( UCoord , VCoord ) ;
}
// Fix the UV coordinates for triangles at the seam where the angle wraps around
for ( const FPolygonID & PolygonID : MeshDescription . Polygons ( ) . GetElementIDs ( ) )
{
const TArray < FVertexInstanceID > & VertexInstances = MeshDescription . GetPolygonPerimeterVertexInstances ( PolygonID ) ;
int32 NumInstances = VertexInstances . Num ( ) ;
if ( NumInstances > = 2 )
{
for ( int32 StartIndex = 1 ; StartIndex < NumInstances ; + + StartIndex )
{
int32 EndIndex = StartIndex + 1 ;
if ( EndIndex > = NumInstances )
{
EndIndex = EndIndex % NumInstances ;
}
const FVector2D & StartUV = OutTexCoords [ VertexInstances [ StartIndex ] . GetValue ( ) ] ;
FVector2D & EndUV = OutTexCoords [ VertexInstances [ EndIndex ] . GetValue ( ) ] ;
// TODO: Improve fix for UVTile other than 1
2018-10-29 10:01:28 -04:00
float Threshold = 0.5f / Params . UVTile . X ;
2018-09-05 10:17:10 -04:00
if ( FMath : : Abs ( EndUV . X - StartUV . X ) > Threshold )
{
// Fix the U coordinate to get the texture go counterclockwise
if ( EndUV . X > Threshold )
{
EndUV . X - = 1.f ;
}
else
{
EndUV . X + = 1.f ;
}
}
}
}
}
}
2018-10-29 10:01:28 -04:00
void FMeshDescriptionOperations : : GenerateBoxUV ( const FMeshDescription & MeshDescription , const FUVMapParameters & Params , TArray < FVector2D > & OutTexCoords )
2018-09-05 10:17:10 -04:00
{
2018-10-29 10:01:28 -04:00
FVector Size = Params . Size * Params . Scale ;
2018-09-05 10:17:10 -04:00
FVector HalfSize = Size / 2.0f ;
2018-10-29 10:01:28 -04:00
FVector Offset = Params . Position - HalfSize ;
2018-09-05 10:17:10 -04:00
2018-10-29 10:01:28 -04:00
FVector HintU = FVector : : UpVector ;
FVector HintV = FVector : : RightVector ;
2018-09-05 10:17:10 -04:00
TMeshAttributesConstRef < FVertexID , FVector > VertexPositions = MeshDescription . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
OutTexCoords . AddZeroed ( MeshDescription . VertexInstances ( ) . Num ( ) ) ;
TArray < FPlane > BoxPlanes ;
2018-10-29 10:01:28 -04:00
const FVector & Center = Params . Position ;
2018-09-05 10:17:10 -04:00
BoxPlanes . Add ( FPlane ( Center + FVector ( 0 , 0 , HalfSize . Z ) , FVector : : UpVector ) ) ; // Top plane
BoxPlanes . Add ( FPlane ( Center - FVector ( 0 , 0 , HalfSize . Z ) , - FVector : : UpVector ) ) ; // Bottom plane
BoxPlanes . Add ( FPlane ( Center + FVector ( HalfSize . X , 0 , 0 ) , FVector : : ForwardVector ) ) ; // Right plane
BoxPlanes . Add ( FPlane ( Center - FVector ( HalfSize . X , 0 , 0 ) , - FVector : : ForwardVector ) ) ; // Left plane
BoxPlanes . Add ( FPlane ( Center + FVector ( 0 , HalfSize . Y , 0 ) , FVector : : RightVector ) ) ; // Front plane
BoxPlanes . Add ( FPlane ( Center - FVector ( 0 , HalfSize . Y , 0 ) , - FVector : : RightVector ) ) ; // Back plane
// For each polygon, find the box plane that best matches the polygon normal
for ( const FPolygonID & PolygonID : MeshDescription . Polygons ( ) . GetElementIDs ( ) )
{
const TArray < FVertexInstanceID > & VertexInstances = MeshDescription . GetPolygonPerimeterVertexInstances ( PolygonID ) ;
check ( VertexInstances . Num ( ) = = 3 ) ;
FVector Vertex0 = VertexPositions [ MeshDescription . GetVertexInstanceVertex ( VertexInstances [ 0 ] ) ] ;
FVector Vertex1 = VertexPositions [ MeshDescription . GetVertexInstanceVertex ( VertexInstances [ 1 ] ) ] ;
FVector Vertex2 = VertexPositions [ MeshDescription . GetVertexInstanceVertex ( VertexInstances [ 2 ] ) ] ;
FPlane PolygonPlane ( Vertex0 , Vertex2 , Vertex1 ) ;
// Find the box plane that is most aligned with the polygon plane
// TODO: Also take the distance between the planes into consideration
float MaxProj = 0.f ;
int32 BestPlaneIndex = 0 ;
for ( int32 Index = 0 ; Index < BoxPlanes . Num ( ) ; + + Index )
{
float Proj = FVector : : DotProduct ( BoxPlanes [ Index ] , PolygonPlane ) ;
if ( Proj > MaxProj )
{
MaxProj = Proj ;
BestPlaneIndex = Index ;
}
}
const FPlane & BestPlane = BoxPlanes [ BestPlaneIndex ] ;
FVector U = HintU ;
FVector V = BestPlane ^ HintU ;
if ( V . IsZero ( ) )
{
// Plane normal and U were aligned, so try with V instead
U = HintV ;
V = BestPlane ^ HintV ;
}
for ( const FVertexInstanceID & VertexInstanceID : VertexInstances )
{
const FVertexID VertexID = MeshDescription . GetVertexInstanceVertex ( VertexInstanceID ) ;
FVector Vertex = VertexPositions [ VertexID ] ;
// Apply the gizmo transforms
2018-10-29 10:01:28 -04:00
Vertex = Params . Rotation . RotateVector ( Vertex ) ;
2018-09-05 10:17:10 -04:00
Vertex - = Offset ;
Vertex / = Size ;
2018-10-29 10:01:28 -04:00
float UCoord = FVector : : DotProduct ( Vertex , U ) * Params . UVTile . X ;
float VCoord = FVector : : DotProduct ( Vertex , V ) * Params . UVTile . Y ;
2018-09-05 10:17:10 -04:00
OutTexCoords [ VertexInstanceID . GetValue ( ) ] = FVector2D ( UCoord , VCoord ) ;
}
}
}
2019-01-02 15:37:07 -05:00
void FMeshDescriptionOperations : : RemapPolygonGroups ( FMeshDescription & MeshDescription , TMap < FPolygonGroupID , FPolygonGroupID > & Remap )
{
TPolygonGroupAttributesRef < FName > PolygonGroupNames = MeshDescription . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
struct FOldPolygonGroupData
{
FName Name ;
TArray < FPolygonID > Polygons ;
} ;
TMap < FPolygonGroupID , FOldPolygonGroupData > OldData ;
for ( const FPolygonGroupID & PolygonGroupID : MeshDescription . PolygonGroups ( ) . GetElementIDs ( ) )
{
if ( ! Remap . Contains ( PolygonGroupID ) | | PolygonGroupID = = Remap [ PolygonGroupID ] )
{
//No need to change this one
continue ;
}
FOldPolygonGroupData & PolygonGroupData = OldData . FindOrAdd ( PolygonGroupID ) ;
PolygonGroupData . Name = PolygonGroupNames [ PolygonGroupID ] ;
FMeshPolygonGroup & PolygonGroup = MeshDescription . GetPolygonGroup ( PolygonGroupID ) ;
PolygonGroupData . Polygons = PolygonGroup . Polygons ;
PolygonGroup . Polygons . Empty ( ) ;
MeshDescription . DeletePolygonGroup ( PolygonGroupID ) ;
}
for ( auto Kvp : OldData )
{
FPolygonGroupID GroupID = Kvp . Key ;
FPolygonGroupID ToGroupID = Remap [ GroupID ] ;
if ( ! MeshDescription . PolygonGroups ( ) . IsValid ( ToGroupID ) )
{
MeshDescription . CreatePolygonGroupWithID ( ToGroupID ) ;
}
TArray < FPolygonID > & Polygons = MeshDescription . GetPolygonGroup ( ToGroupID ) . Polygons ;
Polygons . Append ( Kvp . Value . Polygons ) ;
PolygonGroupNames [ ToGroupID ] = Kvp . Value . Name ;
for ( FPolygonID PolygonID : Polygons )
{
MeshDescription . GetPolygon ( PolygonID ) . PolygonGroupID = ToGroupID ;
}
}
}
void FMeshDescriptionOperations : : SwapPolygonPolygonGroup ( FMeshDescription & MeshDescription , int32 SectionIndex , int32 TriangleIndexStart , int32 TriangleIndexEnd , bool bRemoveEmptyPolygonGroup )
{
int32 TriangleIndex = 0 ;
TPolygonGroupAttributesRef < FName > PolygonGroupNames = MeshDescription . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
FPolygonGroupID TargetPolygonGroupID ( SectionIndex ) ;
if ( ! bRemoveEmptyPolygonGroup )
{
while ( ! MeshDescription . PolygonGroups ( ) . IsValid ( TargetPolygonGroupID ) )
{
TargetPolygonGroupID = MeshDescription . CreatePolygonGroup ( ) ;
PolygonGroupNames [ TargetPolygonGroupID ] = FName ( * ( TEXT ( " SwapPolygonMaterialSlotName_ " ) + FString : : FromInt ( TargetPolygonGroupID . GetValue ( ) ) ) ) ;
TargetPolygonGroupID = FPolygonGroupID ( SectionIndex ) ;
}
}
else
{
//This will not follow the SectionIndex value if the value is greater then the number of section (do not use this when merging meshes)
if ( ! MeshDescription . PolygonGroups ( ) . IsValid ( TargetPolygonGroupID ) )
{
TargetPolygonGroupID = MeshDescription . CreatePolygonGroup ( ) ;
PolygonGroupNames [ TargetPolygonGroupID ] = FName ( * ( TEXT ( " SwapPolygonMaterialSlotName_ " ) + FString : : FromInt ( TargetPolygonGroupID . GetValue ( ) ) ) ) ;
}
}
for ( const FPolygonID PolygonID : MeshDescription . Polygons ( ) . GetElementIDs ( ) )
{
int32 TriangleCount = MeshDescription . GetPolygonTriangles ( PolygonID ) . Num ( ) ;
if ( TriangleIndex > = TriangleIndexStart & & TriangleIndex < TriangleIndexEnd )
{
check ( TriangleIndex + ( TriangleCount - 1 ) < TriangleIndexEnd ) ;
FMeshPolygon & Polygon = MeshDescription . GetPolygon ( PolygonID ) ;
FPolygonGroupID OldpolygonGroupID = Polygon . PolygonGroupID ;
if ( OldpolygonGroupID ! = TargetPolygonGroupID )
{
MeshDescription . SetPolygonPolygonGroup ( PolygonID , TargetPolygonGroupID ) ;
if ( bRemoveEmptyPolygonGroup & & MeshDescription . GetPolygonGroupPolygons ( OldpolygonGroupID ) . Num ( ) < 1 )
{
MeshDescription . DeletePolygonGroup ( OldpolygonGroupID ) ;
}
}
}
TriangleIndex + = TriangleCount ;
}
}
bool FMeshDescriptionOperations : : HasVertexColor ( const FMeshDescription & MeshDescription )
{
TVertexInstanceAttributesConstRef < FVector4 > VertexInstanceColors = MeshDescription . VertexInstanceAttributes ( ) . GetAttributesRef < FVector4 > ( MeshAttribute : : VertexInstance : : Color ) ;
bool bHasVertexColor = false ;
FVector4 WhiteColor ( FLinearColor : : White ) ;
for ( const FVertexInstanceID & VertexInstanceID : MeshDescription . VertexInstances ( ) . GetElementIDs ( ) )
{
if ( VertexInstanceColors [ VertexInstanceID ] ! = WhiteColor )
{
bHasVertexColor = true ;
break ;
}
}
return bHasVertexColor ;
}
void FMeshDescriptionOperations : : BuildWeldedVertexIDRemap ( const FMeshDescription & MeshDescription , const float WeldingThreshold , TMap < FVertexID , FVertexID > & OutVertexIDRemap )
{
TVertexAttributesConstRef < FVector > VertexPositions = MeshDescription . VertexAttributes ( ) . GetAttributesRef < FVector > ( MeshAttribute : : Vertex : : Position ) ;
int32 NumVertex = MeshDescription . Vertices ( ) . Num ( ) ;
OutVertexIDRemap . Reserve ( NumVertex ) ;
// Create a list of vertex Z/index pairs
TArray < MeshDescriptionOperationNamespace : : FIndexAndZ > VertIndexAndZ ;
VertIndexAndZ . Reserve ( NumVertex ) ;
for ( const FVertexID VertexID : MeshDescription . Vertices ( ) . GetElementIDs ( ) )
{
new ( VertIndexAndZ ) MeshDescriptionOperationNamespace : : FIndexAndZ ( VertexID . GetValue ( ) , VertexPositions [ VertexID ] ) ;
}
// Sort the vertices by z value
VertIndexAndZ . Sort ( MeshDescriptionOperationNamespace : : FCompareIndexAndZ ( ) ) ;
// Search for duplicates, quickly!
for ( int32 i = 0 ; i < VertIndexAndZ . Num ( ) ; i + + )
{
FVertexID Index_i = FVertexID ( VertIndexAndZ [ i ] . Index ) ;
if ( OutVertexIDRemap . Contains ( Index_i ) )
{
continue ;
}
OutVertexIDRemap . FindOrAdd ( Index_i ) = Index_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 ) > WeldingThreshold )
break ; // can't be any more dups
const FVector & PositionA = * ( VertIndexAndZ [ i ] . OriginalVector ) ;
const FVector & PositionB = * ( VertIndexAndZ [ j ] . OriginalVector ) ;
if ( PositionA . Equals ( PositionB , WeldingThreshold ) )
{
OutVertexIDRemap . FindOrAdd ( FVertexID ( VertIndexAndZ [ j ] . Index ) ) = Index_i ;
}
}
}
}
2018-02-22 10:04:16 -05:00
# undef LOCTEXT_NAMESPACE