2019-12-27 09:26:59 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-06-04 15:42:48 -04:00
# include "DynamicMeshEditor.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/DynamicMeshAttributeSet.h"
2019-06-04 15:42:48 -04:00
# include "Util/BufferUtil.h"
# include "MeshRegionBoundaryLoops.h"
2019-12-19 18:07:47 -05:00
# include "DynamicSubmesh3.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/DynamicVertexSkinWeightsAttribute.h"
# include "DynamicMesh/MeshNormals.h"
2020-06-23 18:40:00 -04:00
# include "MeshQueries.h"
# include "Selections/MeshConnectedComponents.h"
2019-06-04 15:42:48 -04:00
2021-03-09 19:33:56 -04:00
using namespace UE : : Geometry ;
2019-06-04 15:42:48 -04:00
void FDynamicMeshEditResult : : GetAllTriangles ( TArray < int > & TrianglesOut ) const
{
BufferUtil : : AppendElements ( TrianglesOut , NewTriangles ) ;
int NumQuads = NewQuads . Num ( ) ;
for ( int k = 0 ; k < NumQuads ; + + k )
{
TrianglesOut . Add ( NewQuads [ k ] . A ) ;
TrianglesOut . Add ( NewQuads [ k ] . B ) ;
}
int NumPolys = NewPolygons . Num ( ) ;
for ( int k = 0 ; k < NumPolys ; + + k )
{
BufferUtil : : AppendElements ( TrianglesOut , NewPolygons [ k ] ) ;
}
}
2020-11-01 22:12:11 -04:00
bool FDynamicMeshEditor : : RemoveIsolatedVertices ( )
{
bool bSuccess = true ;
for ( int VID : Mesh - > VertexIndicesItr ( ) )
{
2021-10-12 21:21:22 -04:00
if ( ! Mesh - > IsReferencedVertex ( VID ) )
2020-11-01 22:12:11 -04:00
{
2021-10-12 21:21:22 -04:00
constexpr bool bPreserveManifold = false ;
bSuccess = Mesh - > RemoveVertex ( VID , bPreserveManifold ) = = EMeshResult : : Ok & & bSuccess ;
2020-11-01 22:12:11 -04:00
}
}
return bSuccess ;
}
2019-06-04 15:42:48 -04:00
2022-05-17 10:13:11 -04:00
namespace DynamicMeshEditorLocals
2019-06-04 15:42:48 -04:00
{
2022-05-17 10:13:11 -04:00
// Does all the work of StitchVertexLoopsMinimal and StitchVertexLoopToTriVidPairSequence since they only differ in the way
// that they pick verts per quad.
template < typename FuncType >
bool StitchLoopsInternal ( FDynamicMeshEditor & Editor , int32 NumQuads , FuncType & & GetQuadVidsForIndex , FDynamicMeshEditResult & ResultOut )
{
ResultOut . NewQuads . Reserve ( NumQuads ) ;
ResultOut . NewGroups . Reserve ( NumQuads ) ;
for ( int i = 0 ; i < NumQuads ; + + i )
2019-06-04 15:42:48 -04:00
{
2022-05-17 10:13:11 -04:00
int32 a , b , c , d ;
GetQuadVidsForIndex ( i , a , b , c , d ) ;
2019-06-04 15:42:48 -04:00
2022-05-17 10:13:11 -04:00
int NewGroupID = Editor . Mesh - > AllocateTriangleGroup ( ) ;
2019-06-04 15:42:48 -04:00
ResultOut . NewGroups . Add ( NewGroupID ) ;
FIndex3i t1 ( b , a , d ) ;
2022-05-17 10:13:11 -04:00
int tid1 = Editor . Mesh - > AppendTriangle ( t1 , NewGroupID ) ;
2019-06-04 15:42:48 -04:00
FIndex3i t2 ( a , c , d ) ;
2022-05-17 10:13:11 -04:00
int tid2 = Editor . Mesh - > AppendTriangle ( t2 , NewGroupID ) ;
2019-06-04 15:42:48 -04:00
ResultOut . NewQuads . Add ( FIndex2i ( tid1 , tid2 ) ) ;
if ( tid1 < 0 | | tid2 < 0 )
{
goto operation_failed ;
}
}
return true ;
operation_failed :
// remove what we added so far
2022-05-17 10:13:11 -04:00
if ( ResultOut . NewQuads . Num ( ) )
2019-06-04 15:42:48 -04:00
{
2019-09-10 11:35:20 -04:00
TArray < int > Triangles ; Triangles . Reserve ( 2 * ResultOut . NewQuads . Num ( ) ) ;
for ( const FIndex2i & QuadTriIndices : ResultOut . NewQuads )
{
Triangles . Add ( QuadTriIndices . A ) ;
Triangles . Add ( QuadTriIndices . B ) ;
}
2022-05-17 10:13:11 -04:00
if ( ! Editor . RemoveTriangles ( Triangles , false ) )
2019-06-04 15:42:48 -04:00
{
2022-05-17 10:13:11 -04:00
ensureMsgf ( false , TEXT ( " FDynamicMeshEditor::StitchVertexLoopsMinimal: failed to add all triangles, and also failed to back out changes. " ) ) ;
2019-06-04 15:42:48 -04:00
}
}
return false ;
}
2022-05-17 10:13:11 -04:00
}
2019-06-04 15:42:48 -04:00
2022-05-17 10:13:11 -04:00
bool FDynamicMeshEditor : : StitchVertexLoopsMinimal ( const TArray < int > & Loop1 , const TArray < int > & Loop2 , FDynamicMeshEditResult & ResultOut )
{
int N = Loop1 . Num ( ) ;
if ( ! ensureMsgf ( N = = Loop2 . Num ( ) , TEXT ( " FDynamicMeshEditor::StitchVertexLoopsMinimal: loops are not the same length! " ) ) )
{
return false ;
}
return DynamicMeshEditorLocals : : StitchLoopsInternal ( * this , N , [ N , & Loop1 , & Loop2 ]
( int32 Index , int32 & VertA , int32 & VertB , int32 & VertC , int32 & VertD ) {
VertA = Loop1 [ Index ] ;
VertB = Loop1 [ ( Index + 1 ) % N ] ;
VertC = Loop2 [ Index ] ;
VertD = Loop2 [ ( Index + 1 ) % N ] ;
} , ResultOut ) ;
}
bool FDynamicMeshEditor : : StitchVertexLoopToTriVidPairSequence (
const TArray < TPair < int32 , TPair < int8 , int8 > > > & TriVidPairs ,
const TArray < int > & VertexLoop , FDynamicMeshEditResult & ResultOut )
{
int N = TriVidPairs . Num ( ) ;
if ( ! ensureMsgf ( N = = VertexLoop . Num ( ) , TEXT ( " FDynamicMeshEditor::StitchVertexLoopToEdgeSequence: sequences are not the same length! " ) ) )
{
return false ;
}
return DynamicMeshEditorLocals : : StitchLoopsInternal ( * this , N , [ this , N , & TriVidPairs , & VertexLoop ]
( int32 Index , int32 & VertA , int32 & VertB , int32 & VertC , int32 & VertD ) {
FIndex3i TriVids1 = Mesh - > GetTriangle ( TriVidPairs [ Index ] . Key ) ;
VertA = TriVids1 [ TriVidPairs [ Index ] . Value . Key ] ;
VertB = TriVids1 [ TriVidPairs [ Index ] . Value . Value ] ;
VertC = VertexLoop [ Index ] ;
VertD = VertexLoop [ ( Index + 1 ) % N ] ;
} , ResultOut ) ;
}
bool FDynamicMeshEditor : : ConvertLoopToTriVidPairSequence ( const FDynamicMesh3 & Mesh , const TArray < int32 > & VidLoop ,
const TArray < int32 > & EdgeLoop , TArray < TPair < int32 , TPair < int8 , int8 > > > & TriVertPairsOut )
{
if ( ! ensure ( EdgeLoop . Num ( ) = = VidLoop . Num ( ) ) )
{
return false ;
}
for ( int32 QuadIndex = 0 ; QuadIndex < EdgeLoop . Num ( ) ; + + QuadIndex )
{
int32 Tid = Mesh . GetEdgeT ( EdgeLoop [ QuadIndex ] ) . A ;
int32 FirstVid = VidLoop [ QuadIndex ] ;
int32 SecondVid = VidLoop [ ( QuadIndex + 1 ) % VidLoop . Num ( ) ] ;
FIndex3i TriVids = Mesh . GetTriangle ( Tid ) ;
int8 SubIdx1 = IndexUtil : : FindTriIndex ( FirstVid , TriVids ) ;
int8 SubIdx2 = IndexUtil : : FindTriIndex ( SecondVid , TriVids ) ;
if ( ensure ( SubIdx1 > = 0 & & SubIdx2 > = 0 ) )
{
TriVertPairsOut . Emplace ( Tid ,
TPair < int8 , int8 > ( SubIdx1 , SubIdx2 ) ) ;
}
else
{
return false ;
}
}
return true ;
}
2019-06-04 15:42:48 -04:00
2020-06-23 18:40:00 -04:00
bool FDynamicMeshEditor : : WeldVertexLoops ( const TArray < int32 > & Loop1 , const TArray < int32 > & Loop2 )
{
int32 N = Loop1 . Num ( ) ;
checkf ( N = = Loop2 . Num ( ) , TEXT ( " FDynamicMeshEditor::WeldVertexLoops: loops are not the same length! " ) ) ;
if ( N ! = Loop2 . Num ( ) )
{
return false ;
}
int32 FailureCount = 0 ;
// collect set of edges
TArray < int32 > Edges1 , Edges2 ;
Edges1 . SetNum ( N ) ;
Edges2 . SetNum ( N ) ;
for ( int32 i = 0 ; i < N ; + + i )
{
int32 a = Loop1 [ i ] ;
int32 b = Loop1 [ ( i + 1 ) % N ] ;
Edges1 [ i ] = Mesh - > FindEdge ( a , b ) ;
if ( Edges1 [ i ] = = FDynamicMesh3 : : InvalidID )
{
return false ;
}
int32 c = Loop2 [ i ] ;
int32 d = Loop2 [ ( i + 1 ) % N ] ;
Edges2 [ i ] = Mesh - > FindEdge ( c , d ) ;
if ( Edges2 [ i ] = = FDynamicMesh3 : : InvalidID )
{
return false ;
}
}
// merge edges. Some merges may merge multiple edges, in which case we want to
// skip those when we encounter them later.
TArray < int32 > SkipEdges ;
for ( int32 i = 0 ; i < N ; + + i )
{
int32 Edge1 = Edges1 [ i ] ;
int32 Edge2 = Edges2 [ i ] ;
if ( SkipEdges . Contains ( Edge2 ) ) // occurs at loop closures
{
continue ;
}
FDynamicMesh3 : : FMergeEdgesInfo MergeInfo ;
EMeshResult Result = Mesh - > MergeEdges ( Edge1 , Edge2 , MergeInfo ) ;
if ( Result ! = EMeshResult : : Ok )
{
FailureCount + + ;
}
else
{
if ( MergeInfo . ExtraRemovedEdges . A ! = FDynamicMesh3 : : InvalidID )
{
SkipEdges . Add ( MergeInfo . ExtraRemovedEdges . A ) ;
}
if ( MergeInfo . ExtraRemovedEdges . B ! = FDynamicMesh3 : : InvalidID )
{
SkipEdges . Add ( MergeInfo . ExtraRemovedEdges . B ) ;
}
}
}
return ( FailureCount > 0 ) ;
}
2020-11-01 22:12:11 -04:00
bool FDynamicMeshEditor : : StitchSparselyCorrespondedVertexLoops ( const TArray < int > & VertexIDs1 , const TArray < int > & MatchedIndices1 , const TArray < int > & VertexIDs2 , const TArray < int > & MatchedIndices2 , FDynamicMeshEditResult & ResultOut , bool bReverseOrientation )
2019-09-10 11:35:20 -04:00
{
int CorrespondN = MatchedIndices1 . Num ( ) ;
if ( ! ensureMsgf ( CorrespondN = = MatchedIndices2 . Num ( ) , TEXT ( " FDynamicMeshEditor::StitchSparselyCorrespondedVertices: correspondence arrays are not the same length! " ) ) )
{
return false ;
}
// TODO: support case of only one corresponded vertex & a connecting a full loop around?
// this requires allowing start==end to not immediately stop the walk ...
if ( ! ensureMsgf ( CorrespondN > = 2 , TEXT ( " Must have at least two corresponded vertices " ) ) )
{
return false ;
}
ResultOut . NewGroups . Reserve ( CorrespondN ) ;
int i = 0 ;
for ( ; i < CorrespondN ; + + i )
{
int Starts [ 2 ] { MatchedIndices1 [ i ] , MatchedIndices2 [ i ] } ;
int Ends [ 2 ] { MatchedIndices1 [ ( i + 1 ) % CorrespondN ] , MatchedIndices2 [ ( i + 1 ) % CorrespondN ] } ;
auto GetWrappedSpanLen = [ ] ( const FDynamicMesh3 * M , const TArray < int > & VertexIDs , int StartInd , int EndInd )
{
float LenTotal = 0 ;
FVector3d V = M - > GetVertex ( VertexIDs [ StartInd ] ) ;
for ( int Ind = StartInd , IndNext ; Ind ! = EndInd ; )
{
IndNext = ( Ind + 1 ) % VertexIDs . Num ( ) ;
FVector3d VNext = M - > GetVertex ( VertexIDs [ IndNext ] ) ;
2021-03-30 21:25:22 -04:00
LenTotal + = Distance ( V , VNext ) ;
2019-09-10 11:35:20 -04:00
Ind = IndNext ;
V = VNext ;
}
return LenTotal ;
} ;
float LenTotal [ 2 ] { GetWrappedSpanLen ( Mesh , VertexIDs1 , Starts [ 0 ] , Ends [ 0 ] ) , GetWrappedSpanLen ( Mesh , VertexIDs2 , Starts [ 1 ] , Ends [ 1 ] ) } ;
float LenAlong [ 2 ] { FMathf : : Epsilon , FMathf : : Epsilon } ;
LenTotal [ 0 ] + = FMathf : : Epsilon ;
LenTotal [ 1 ] + = FMathf : : Epsilon ;
int NewGroupID = Mesh - > AllocateTriangleGroup ( ) ;
ResultOut . NewGroups . Add ( NewGroupID ) ;
int Walks [ 2 ] { Starts [ 0 ] , Starts [ 1 ] } ;
FVector3d Vertex [ 2 ] { Mesh - > GetVertex ( VertexIDs1 [ Starts [ 0 ] ] ) , Mesh - > GetVertex ( VertexIDs2 [ Starts [ 1 ] ] ) } ;
while ( Walks [ 0 ] ! = Ends [ 0 ] | | Walks [ 1 ] ! = Ends [ 1 ] )
{
float PctAlong [ 2 ] { LenAlong [ 0 ] / LenTotal [ 0 ] , LenAlong [ 1 ] / LenTotal [ 1 ] } ;
bool bAdvanceSecond = ( Walks [ 0 ] = = Ends [ 0 ] | | ( Walks [ 1 ] ! = Ends [ 1 ] & & PctAlong [ 0 ] > PctAlong [ 1 ] ) ) ;
FIndex3i Tri ( VertexIDs1 [ Walks [ 0 ] ] , VertexIDs2 [ Walks [ 1 ] ] , - 1 ) ;
if ( ! bAdvanceSecond )
{
Walks [ 0 ] = ( Walks [ 0 ] + 1 ) % VertexIDs1 . Num ( ) ;
Tri . C = VertexIDs1 [ Walks [ 0 ] ] ;
FVector3d NextV = Mesh - > GetVertex ( Tri . C ) ;
2021-03-30 21:25:22 -04:00
LenAlong [ 0 ] + = Distance ( NextV , Vertex [ 0 ] ) ;
2019-09-10 11:35:20 -04:00
Vertex [ 0 ] = NextV ;
}
else
{
Walks [ 1 ] = ( Walks [ 1 ] + 1 ) % VertexIDs2 . Num ( ) ;
Tri . C = VertexIDs2 [ Walks [ 1 ] ] ;
FVector3d NextV = Mesh - > GetVertex ( Tri . C ) ;
2021-03-30 21:25:22 -04:00
LenAlong [ 1 ] + = Distance ( NextV , Vertex [ 1 ] ) ;
2019-09-10 11:35:20 -04:00
Vertex [ 1 ] = NextV ;
}
2020-11-01 22:12:11 -04:00
if ( bReverseOrientation )
{
Swap ( Tri . B , Tri . C ) ;
}
2019-09-10 11:35:20 -04:00
int Tid = Mesh - > AppendTriangle ( Tri , NewGroupID ) ;
if ( Tid < 0 )
{
goto operation_failed ;
}
2020-01-27 20:11:15 -05:00
ResultOut . NewTriangles . Add ( Tid ) ;
2019-09-10 11:35:20 -04:00
}
}
return true ;
operation_failed :
// remove what we added so far
2020-01-27 20:11:15 -05:00
if ( ResultOut . NewTriangles . Num ( ) )
2019-09-10 11:35:20 -04:00
{
ensureMsgf ( RemoveTriangles ( ResultOut . NewTriangles , false ) , TEXT ( " FDynamicMeshEditor::StitchSparselyCorrespondedVertexLoops: failed to add all triangles, and also failed to back out changes. " ) ) ;
}
return false ;
}
bool FDynamicMeshEditor : : AddTriangleFan_OrderedVertexLoop ( int CenterVertex , const TArray < int > & VertexLoop , int GroupID , FDynamicMeshEditResult & ResultOut )
{
if ( GroupID = = - 1 )
{
GroupID = Mesh - > AllocateTriangleGroup ( ) ;
ResultOut . NewGroups . Add ( GroupID ) ;
}
int N = VertexLoop . Num ( ) ;
ResultOut . NewTriangles . Reserve ( N ) ;
int i = 0 ;
for ( i = 0 ; i < N ; + + i )
{
int A = VertexLoop [ i ] ;
int B = VertexLoop [ ( i + 1 ) % N ] ;
FIndex3i NewT ( CenterVertex , B , A ) ;
int NewTID = Mesh - > AppendTriangle ( NewT , GroupID ) ;
if ( NewTID < 0 )
{
goto operation_failed ;
}
ResultOut . NewTriangles . Add ( NewTID ) ;
}
return true ;
operation_failed :
// remove what we added so far
if ( ! RemoveTriangles ( ResultOut . NewTriangles , false ) )
{
checkf ( false , TEXT ( " FDynamicMeshEditor::AddTriangleFan: failed to add all triangles, and also failed to back out changes. " ) ) ;
}
return false ;
}
2019-06-04 15:42:48 -04:00
bool FDynamicMeshEditor : : RemoveTriangles ( const TArray < int > & Triangles , bool bRemoveIsolatedVerts )
2019-10-01 20:41:42 -04:00
{
return RemoveTriangles ( Triangles , bRemoveIsolatedVerts , [ ] ( int ) { } ) ;
}
bool FDynamicMeshEditor : : RemoveTriangles ( const TArray < int > & Triangles , bool bRemoveIsolatedVerts , TFunctionRef < void ( int ) > OnRemoveTriFunc )
2019-06-04 15:42:48 -04:00
{
bool bAllOK = true ;
int NumTriangles = Triangles . Num ( ) ;
2019-10-01 20:41:42 -04:00
for ( int i = 0 ; i < NumTriangles ; + + i )
2019-06-04 15:42:48 -04:00
{
if ( Mesh - > IsTriangle ( Triangles [ i ] ) = = false )
{
continue ;
}
2019-10-01 20:41:42 -04:00
OnRemoveTriFunc ( Triangles [ i ] ) ;
2019-06-04 15:42:48 -04:00
EMeshResult result = Mesh - > RemoveTriangle ( Triangles [ i ] , bRemoveIsolatedVerts , false ) ;
if ( result ! = EMeshResult : : Ok )
{
bAllOK = false ;
}
}
return bAllOK ;
}
2020-11-10 17:04:15 -04:00
int FDynamicMeshEditor : : RemoveSmallComponents ( double MinVolume , double MinArea , int MinTriangleCount )
2020-06-23 18:40:00 -04:00
{
FMeshConnectedComponents C ( Mesh ) ;
C . FindConnectedTriangles ( ) ;
if ( C . Num ( ) = = 1 )
{
return 0 ;
}
int Removed = 0 ;
for ( FMeshConnectedComponents : : FComponent & Comp : C ) {
2020-11-10 17:04:15 -04:00
bool bRemove = Comp . Indices . Num ( ) < MinTriangleCount ;
if ( ! bRemove )
{
FVector2d VolArea = TMeshQueries < FDynamicMesh3 > : : GetVolumeArea ( * Mesh , Comp . Indices ) ;
bRemove = VolArea . X < MinVolume | | VolArea . Y < MinArea ;
}
if ( bRemove ) {
2020-06-23 18:40:00 -04:00
RemoveTriangles ( Comp . Indices , true ) ;
Removed + + ;
}
}
return Removed ;
}
2019-06-04 15:42:48 -04:00
/**
* Make a copy of provided triangles , with new vertices . You provide IndexMaps because
* you know if you are doing a small subset or a full - mesh - copy .
*/
void FDynamicMeshEditor : : DuplicateTriangles ( const TArray < int > & Triangles , FMeshIndexMappings & IndexMaps , FDynamicMeshEditResult & ResultOut )
{
ResultOut . Reset ( ) ;
IndexMaps . Initialize ( Mesh ) ;
for ( int TriangleID : Triangles )
{
FIndex3i Tri = Mesh - > GetTriangle ( TriangleID ) ;
2020-04-18 18:42:59 -04:00
int NewGroupID = Mesh - > HasTriangleGroups ( ) ? FindOrCreateDuplicateGroup ( TriangleID , IndexMaps , ResultOut ) : - 1 ;
2019-06-04 15:42:48 -04:00
FIndex3i NewTri ;
NewTri [ 0 ] = FindOrCreateDuplicateVertex ( Tri [ 0 ] , IndexMaps , ResultOut ) ;
NewTri [ 1 ] = FindOrCreateDuplicateVertex ( Tri [ 1 ] , IndexMaps , ResultOut ) ;
NewTri [ 2 ] = FindOrCreateDuplicateVertex ( Tri [ 2 ] , IndexMaps , ResultOut ) ;
int NewTriangleID = Mesh - > AppendTriangle ( NewTri , NewGroupID ) ;
IndexMaps . SetTriangle ( TriangleID , NewTriangleID ) ;
ResultOut . NewTriangles . Add ( NewTriangleID ) ;
CopyAttributes ( TriangleID , NewTriangleID , IndexMaps , ResultOut ) ;
//Mesh->CheckValidity(true);
}
}
2019-10-01 20:41:42 -04:00
2020-01-27 20:11:15 -05:00
bool FDynamicMeshEditor : : DisconnectTriangles ( const TArray < int > & Triangles , TArray < FLoopPairSet > & LoopSetOut , bool bHandleBoundaryVertices )
2019-06-04 15:42:48 -04:00
{
// find the region boundary loops
FMeshRegionBoundaryLoops RegionLoops ( Mesh , Triangles , false ) ;
bool bOK = RegionLoops . Compute ( ) ;
2019-12-19 18:07:47 -05:00
if ( ! ensure ( bOK ) )
2019-06-04 15:42:48 -04:00
{
return false ;
}
TArray < FEdgeLoop > & Loops = RegionLoops . Loops ;
// need to test Contains() many times
TSet < int > TriangleSet ;
TriangleSet . Reserve ( Triangles . Num ( ) * 3 ) ;
for ( int TriID : Triangles )
{
TriangleSet . Add ( TriID ) ;
}
// process each loop island
int NumLoops = Loops . Num ( ) ;
LoopSetOut . SetNum ( NumLoops ) ;
2020-01-27 20:11:15 -05:00
TArray < int > FilteredTriangles ;
2022-05-17 10:13:11 -04:00
TMap < int32 , int32 > OldVidsToNewVids ;
2019-06-04 15:42:48 -04:00
for ( int li = 0 ; li < NumLoops ; + + li )
{
FEdgeLoop & Loop = Loops [ li ] ;
FLoopPairSet & LoopPair = LoopSetOut [ li ] ;
2020-01-27 20:11:15 -05:00
LoopPair . OuterVertices = Loop . Vertices ;
LoopPair . OuterEdges = Loop . Edges ;
bool bSawBoundaryInLoop = false ;
2019-06-04 15:42:48 -04:00
// duplicate the vertices
int NumVertices = Loop . Vertices . Num ( ) ;
TArray < int > NewVertexLoop ; NewVertexLoop . SetNum ( NumVertices ) ;
for ( int vi = 0 ; vi < NumVertices ; + + vi )
{
int VertID = Loop . Vertices [ vi ] ;
2022-05-17 10:13:11 -04:00
// See if we've processed this vertex already as part of a bowtie
int32 * ExistingNewVertID = OldVidsToNewVids . Find ( VertID ) ;
if ( ExistingNewVertID )
{
// We've already done the split, but we still need to update the loop pairs. The way we
// do this depends on whether the vert was originally a boundary vert or not, because
// we treat these cases differently when we perform splits.
// If it was originally a boundary vert, the new vertex ended up as an isolated
// vertex.
if ( ! Mesh - > IsReferencedVertex ( * ExistingNewVertID ) )
{
// The isolated vertex should be in the outer loop
LoopPair . OuterVertices [ vi ] = * ExistingNewVertID ;
LoopPair . OuterEdges [ vi ] = FDynamicMesh3 : : InvalidID ;
LoopPair . OuterEdges [ ( vi = = 0 ) ? NumVertices - 1 : vi - 1 ] = FDynamicMesh3 : : InvalidID ;
NewVertexLoop [ vi ] = VertID ;
}
else
{
// Otherwise, the new vertex should end up in the inner loop (and not isolated, because
// it is attached to the selected triangles).
NewVertexLoop [ vi ] = * ExistingNewVertID ;
}
continue ;
}
// If we get here, we still need to split the vertex.
// See how many of the adjacent triangles are in our selection.
2020-01-27 20:11:15 -05:00
FilteredTriangles . Reset ( ) ;
int TriRingCount = 0 ;
for ( int RingTID : Mesh - > VtxTrianglesItr ( VertID ) )
2019-06-04 15:42:48 -04:00
{
2020-01-27 20:11:15 -05:00
if ( TriangleSet . Contains ( RingTID ) )
2019-06-04 15:42:48 -04:00
{
2020-01-27 20:11:15 -05:00
FilteredTriangles . Add ( RingTID ) ;
2019-06-04 15:42:48 -04:00
}
2020-01-27 20:11:15 -05:00
TriRingCount + + ;
2019-06-04 15:42:48 -04:00
}
2022-05-17 10:13:11 -04:00
if ( FilteredTriangles . Num ( ) < TriRingCount )
2019-06-04 15:42:48 -04:00
{
2022-05-17 10:13:11 -04:00
// This is a non-mesh-boundary vert
2020-01-27 20:11:15 -05:00
checkSlow ( ! Mesh - > SplitVertexWouldLeaveIsolated ( VertID , FilteredTriangles ) ) ;
DynamicMeshInfo : : FVertexSplitInfo SplitInfo ;
2022-05-06 15:39:11 -04:00
const EMeshResult MeshResult = Mesh - > SplitVertex ( VertID , FilteredTriangles , SplitInfo ) ;
ensure ( MeshResult = = EMeshResult : : Ok ) ;
2020-01-27 20:11:15 -05:00
int NewVertID = SplitInfo . NewVertex ;
2022-05-17 10:13:11 -04:00
OldVidsToNewVids . Add ( VertID , NewVertID ) ;
2020-01-27 20:11:15 -05:00
NewVertexLoop [ vi ] = NewVertID ;
}
else if ( bHandleBoundaryVertices )
{
// if we have a boundary vertex, we are going to duplicate it and use the duplicated
// vertex as the "old" one, and just keep the existing one on the "inner" loop.
// This means we have to rewrite vertex in the "outer" loop, and that loop will no longer actually be an EdgeLoop, so we set those edges to invalid
int32 NewVertID = Mesh - > AppendVertex ( * Mesh , VertID ) ;
2022-05-17 10:13:11 -04:00
OldVidsToNewVids . Add ( VertID , NewVertID ) ;
2020-01-27 20:11:15 -05:00
LoopPair . OuterVertices [ vi ] = NewVertID ;
LoopPair . OuterEdges [ vi ] = FDynamicMesh3 : : InvalidID ;
LoopPair . OuterEdges [ ( vi = = 0 ) ? NumVertices - 1 : vi - 1 ] = FDynamicMesh3 : : InvalidID ;
NewVertexLoop [ vi ] = VertID ;
bSawBoundaryInLoop = true ;
}
else
{
ensure ( false ) ;
return false ; // cannot proceed
2019-06-04 15:42:48 -04:00
}
}
2020-01-27 20:11:15 -05:00
FEdgeLoop InnerLoop ;
2020-02-14 18:50:11 -05:00
if ( ! ensure ( InnerLoop . InitializeFromVertices ( Mesh , NewVertexLoop , false ) ) )
{
return false ;
}
2020-01-27 20:11:15 -05:00
LoopPair . InnerVertices = MoveTemp ( InnerLoop . Vertices ) ;
LoopPair . InnerEdges = MoveTemp ( InnerLoop . Edges ) ;
LoopPair . bOuterIncludesIsolatedVertices = bSawBoundaryInLoop ;
2019-06-04 15:42:48 -04:00
}
return true ;
}
2019-12-19 18:07:47 -05:00
void FDynamicMeshEditor : : DisconnectTriangles ( const TArray < int > & Triangles , bool bPreventBowties )
{
TSet < int > TriSet , BoundaryVerts ;
TArray < int > NewVerts , OldVertsThatSplit ;
TArray < int > FilteredTriangles ;
DynamicMeshInfo : : FVertexSplitInfo SplitInfo ;
TriSet . Append ( Triangles ) ;
for ( int TID : Triangles )
{
FIndex3i Nbrs = Mesh - > GetTriNeighbourTris ( TID ) ;
FIndex3i Tri = Mesh - > GetTriangle ( TID ) ;
for ( int SubIdx = 0 ; SubIdx < 3 ; SubIdx + + )
{
int NeighborTID = Nbrs [ SubIdx ] ;
if ( ! TriSet . Contains ( NeighborTID ) )
{
BoundaryVerts . Add ( Tri [ SubIdx ] ) ;
BoundaryVerts . Add ( Tri [ ( SubIdx + 1 ) % 3 ] ) ;
}
}
}
for ( int VID : BoundaryVerts )
{
FilteredTriangles . Reset ( ) ;
int TriRingCount = 0 ;
for ( int RingTID : Mesh - > VtxTrianglesItr ( VID ) )
{
if ( TriSet . Contains ( RingTID ) )
{
FilteredTriangles . Add ( RingTID ) ;
}
TriRingCount + + ;
}
if ( FilteredTriangles . Num ( ) < TriRingCount )
{
checkSlow ( ! Mesh - > SplitVertexWouldLeaveIsolated ( VID , FilteredTriangles ) ) ;
ensure ( EMeshResult : : Ok = = Mesh - > SplitVertex ( VID , FilteredTriangles , SplitInfo ) ) ;
NewVerts . Add ( SplitInfo . NewVertex ) ;
OldVertsThatSplit . Add ( SplitInfo . OriginalVertex ) ;
}
}
if ( bPreventBowties )
{
FDynamicMeshEditResult Result ;
for ( int VID : OldVertsThatSplit )
{
SplitBowties ( VID , Result ) ;
Result . Reset ( ) ; // don't actually keep results; they are not used in this fn
}
for ( int VID : NewVerts )
{
SplitBowties ( VID , Result ) ;
Result . Reset ( ) ; // don't actually keep results; they are not used in this fn
}
}
}
void FDynamicMeshEditor : : SplitBowties ( FDynamicMeshEditResult & ResultOut )
{
ResultOut . Reset ( ) ;
TSet < int > AddedVerticesWithIDLessThanMax ; // added vertices that we can't filter just by checking against original max id; this will be empty for compact meshes
for ( int VertexID = 0 , OriginalMaxID = Mesh - > MaxVertexID ( ) ; VertexID < OriginalMaxID ; VertexID + + )
{
if ( ! Mesh - > IsVertex ( VertexID ) | | AddedVerticesWithIDLessThanMax . Contains ( VertexID ) )
{
continue ;
}
int32 NumVertsBefore = ResultOut . NewVertices . Num ( ) ;
// TODO: may be faster to inline this call to reuse the contiguous triangle arrays?
SplitBowties ( VertexID , ResultOut ) ;
for ( int Idx = NumVertsBefore ; Idx < ResultOut . NewVertices . Num ( ) ; Idx + + )
{
if ( ResultOut . NewVertices [ Idx ] < OriginalMaxID )
{
AddedVerticesWithIDLessThanMax . Add ( ResultOut . NewVertices [ Idx ] ) ;
}
}
}
}
void FDynamicMeshEditor : : SplitBowties ( int VertexID , FDynamicMeshEditResult & ResultOut )
{
TArray < int > TrianglesOut , ContiguousGroupLengths ;
TArray < bool > GroupIsLoop ;
DynamicMeshInfo : : FVertexSplitInfo SplitInfo ;
check ( Mesh - > IsVertex ( VertexID ) ) ;
if ( ensure ( EMeshResult : : Ok = = Mesh - > GetVtxContiguousTriangles ( VertexID , TrianglesOut , ContiguousGroupLengths , GroupIsLoop ) ) )
{
if ( ContiguousGroupLengths . Num ( ) > 1 )
{
// is bowtie
for ( int GroupIdx = 1 , GroupStartIdx = ContiguousGroupLengths [ 0 ] ; GroupIdx < ContiguousGroupLengths . Num ( ) ; GroupStartIdx + = ContiguousGroupLengths [ GroupIdx + + ] )
{
ensure ( EMeshResult : : Ok = = Mesh - > SplitVertex ( VertexID , TArrayView < const int > ( TrianglesOut . GetData ( ) + GroupStartIdx , ContiguousGroupLengths [ GroupIdx ] ) , SplitInfo ) ) ;
ResultOut . NewVertices . Add ( SplitInfo . NewVertex ) ;
}
}
}
}
2022-05-17 10:13:11 -04:00
void FDynamicMeshEditor : : SplitBowtiesAtTriangles ( const TArray < int32 > & TriangleIDs , FDynamicMeshEditResult & ResultOut )
{
TSet < int32 > VidsProcessed ;
for ( int32 Tid : TriangleIDs )
{
FIndex3i TriVids = Mesh - > GetTriangle ( Tid ) ;
for ( int i = 0 ; i < 3 ; + + i )
{
int32 Vid = TriVids [ i ] ;
bool bWasAlreadyProcessed = false ;
VidsProcessed . Add ( Vid , & bWasAlreadyProcessed ) ;
if ( ! bWasAlreadyProcessed )
{
SplitBowties ( Vid , ResultOut ) ;
}
}
}
}
2019-12-19 18:07:47 -05:00
bool FDynamicMeshEditor : : ReinsertSubmesh ( const FDynamicSubmesh3 & Region , FOptionallySparseIndexMap & SubToNewV , TArray < int > * new_tris , EDuplicateTriBehavior DuplicateBehavior )
{
check ( Region . GetBaseMesh ( ) = = Mesh ) ;
const FDynamicMesh3 & Sub = Region . GetSubmesh ( ) ;
bool bAllOK = true ;
FIndexFlagSet done_v ( Sub . MaxVertexID ( ) , Sub . TriangleCount ( ) / 2 ) ;
SubToNewV . Initialize ( Sub . MaxVertexID ( ) , Sub . VertexCount ( ) ) ;
int NT = Sub . MaxTriangleID ( ) ;
for ( int ti = 0 ; ti < NT ; + + ti )
{
if ( Sub . IsTriangle ( ti ) = = false )
{
continue ;
}
FIndex3i sub_t = Sub . GetTriangle ( ti ) ;
int gid = Sub . GetTriangleGroup ( ti ) ;
FIndex3i new_t = FIndex3i : : Zero ( ) ;
for ( int j = 0 ; j < 3 ; + + j )
{
int sub_v = sub_t [ j ] ;
int new_v = - 1 ;
if ( done_v [ sub_v ] = = false )
{
// first check if this is a boundary vtx on submesh and maps to a bdry vtx on base mesh
if ( Sub . IsBoundaryVertex ( sub_v ) )
{
int base_v = Region . MapVertexToBaseMesh ( sub_v ) ;
if ( base_v > = 0 & & Mesh - > IsVertex ( base_v ) & & Region . InBaseBorderVertices ( base_v ) = = true )
{
2020-04-18 18:42:59 -04:00
// this should always be true
2019-12-19 18:07:47 -05:00
if ( ensure ( Mesh - > IsBoundaryVertex ( base_v ) ) )
{
new_v = base_v ;
}
}
}
// if that didn't happen, append new vtx
if ( new_v = = - 1 )
{
new_v = Mesh - > AppendVertex ( Sub , sub_v ) ;
}
SubToNewV . Set ( sub_v , new_v ) ;
done_v . Add ( sub_v ) ;
}
else
{
new_v = SubToNewV [ sub_v ] ;
}
new_t [ j ] = new_v ;
}
// try to handle duplicate-tri case
if ( DuplicateBehavior = = EDuplicateTriBehavior : : EnsureContinue )
{
ensure ( Mesh - > FindTriangle ( new_t . A , new_t . B , new_t . C ) = = FDynamicMesh3 : : InvalidID ) ;
}
else
{
int existing_tid = Mesh - > FindTriangle ( new_t . A , new_t . B , new_t . C ) ;
if ( existing_tid ! = FDynamicMesh3 : : InvalidID )
{
if ( DuplicateBehavior = = EDuplicateTriBehavior : : EnsureAbort )
{
ensure ( false ) ;
return false ;
}
else if ( DuplicateBehavior = = EDuplicateTriBehavior : : UseExisting )
{
if ( new_tris )
{
new_tris - > Add ( existing_tid ) ;
}
continue ;
}
else if ( DuplicateBehavior = = EDuplicateTriBehavior : : Replace )
{
Mesh - > RemoveTriangle ( existing_tid , false ) ;
}
}
}
int new_tid = Mesh - > AppendTriangle ( new_t , gid ) ;
2020-02-14 18:31:22 -05:00
ensure ( new_tid > = 0 ) ;
2019-12-19 18:07:47 -05:00
if ( ! Mesh - > IsTriangle ( new_tid ) )
{
bAllOK = false ;
}
if ( new_tris )
{
new_tris - > Add ( new_tid ) ;
}
}
return bAllOK ;
}
2019-06-04 15:42:48 -04:00
FVector3f FDynamicMeshEditor : : ComputeAndSetQuadNormal ( const FIndex2i & QuadTris , bool bIsPlanar )
{
FVector3f Normal ( 0 , 0 , 1 ) ;
if ( bIsPlanar )
{
Normal = ( FVector3f ) Mesh - > GetTriNormal ( QuadTris . A ) ;
}
else
{
Normal = ( FVector3f ) Mesh - > GetTriNormal ( QuadTris . A ) ;
Normal + = ( FVector3f ) Mesh - > GetTriNormal ( QuadTris . B ) ;
2021-03-17 19:32:44 -04:00
Normalize ( Normal ) ;
2019-06-04 15:42:48 -04:00
}
SetQuadNormals ( QuadTris , Normal ) ;
return Normal ;
}
void FDynamicMeshEditor : : SetQuadNormals ( const FIndex2i & QuadTris , const FVector3f & Normal )
{
check ( Mesh - > HasAttributes ( ) ) ;
FDynamicMeshNormalOverlay * Normals = Mesh - > Attributes ( ) - > PrimaryNormals ( ) ;
FIndex3i Triangle1 = Mesh - > GetTriangle ( QuadTris . A ) ;
FIndex3i NormalTriangle1 ;
2020-04-18 18:42:59 -04:00
NormalTriangle1 [ 0 ] = Normals - > AppendElement ( Normal ) ;
NormalTriangle1 [ 1 ] = Normals - > AppendElement ( Normal ) ;
NormalTriangle1 [ 2 ] = Normals - > AppendElement ( Normal ) ;
2019-06-04 15:42:48 -04:00
Normals - > SetTriangle ( QuadTris . A , NormalTriangle1 ) ;
if ( Mesh - > IsTriangle ( QuadTris . B ) )
{
FIndex3i Triangle2 = Mesh - > GetTriangle ( QuadTris . B ) ;
FIndex3i NormalTriangle2 ;
for ( int j = 0 ; j < 3 ; + + j )
{
int i = Triangle1 . IndexOf ( Triangle2 [ j ] ) ;
if ( i = = - 1 )
{
2020-04-18 18:42:59 -04:00
NormalTriangle2 [ j ] = Normals - > AppendElement ( Normal ) ;
2019-06-04 15:42:48 -04:00
}
else
{
NormalTriangle2 [ j ] = NormalTriangle1 [ i ] ;
}
}
Normals - > SetTriangle ( QuadTris . B , NormalTriangle2 ) ;
}
}
void FDynamicMeshEditor : : SetTriangleNormals ( const TArray < int > & Triangles , const FVector3f & Normal )
{
check ( Mesh - > HasAttributes ( ) ) ;
FDynamicMeshNormalOverlay * Normals = Mesh - > Attributes ( ) - > PrimaryNormals ( ) ;
TMap < int , int > Vertices ;
for ( int tid : Triangles )
{
2020-08-11 01:36:57 -04:00
if ( Normals - > IsSetTriangle ( tid ) )
{
Normals - > UnsetTriangle ( tid ) ;
}
2019-06-04 15:42:48 -04:00
FIndex3i BaseTri = Mesh - > GetTriangle ( tid ) ;
FIndex3i ElemTri ;
for ( int j = 0 ; j < 3 ; + + j )
{
const int * FoundElementID = Vertices . Find ( BaseTri [ j ] ) ;
if ( FoundElementID = = nullptr )
{
2020-04-18 18:42:59 -04:00
ElemTri [ j ] = Normals - > AppendElement ( Normal ) ;
2019-06-04 15:42:48 -04:00
Vertices . Add ( BaseTri [ j ] , ElemTri [ j ] ) ;
}
else
{
ElemTri [ j ] = * FoundElementID ;
}
}
Normals - > SetTriangle ( tid , ElemTri ) ;
}
}
2020-01-27 20:11:15 -05:00
void FDynamicMeshEditor : : SetTriangleNormals ( const TArray < int > & Triangles )
{
check ( Mesh - > HasAttributes ( ) ) ;
FDynamicMeshNormalOverlay * Normals = Mesh - > Attributes ( ) - > PrimaryNormals ( ) ;
TSet < int32 > TriangleSet ( Triangles ) ;
TUniqueFunction < bool ( int32 ) > TrianglePredicate = [ & ] ( int32 TriangleID ) { return TriangleSet . Contains ( TriangleID ) ; } ;
TMap < int , int > Vertices ;
for ( int tid : Triangles )
{
2020-08-11 01:36:57 -04:00
if ( Normals - > IsSetTriangle ( tid ) )
{
Normals - > UnsetTriangle ( tid ) ;
}
2020-01-27 20:11:15 -05:00
FIndex3i BaseTri = Mesh - > GetTriangle ( tid ) ;
FIndex3i ElemTri ;
for ( int j = 0 ; j < 3 ; + + j )
{
const int * FoundElementID = Vertices . Find ( BaseTri [ j ] ) ;
if ( FoundElementID = = nullptr )
{
FVector3d VtxROINormal = FMeshNormals : : ComputeVertexNormal ( * Mesh , BaseTri [ j ] , TrianglePredicate ) ;
2020-04-18 18:42:59 -04:00
ElemTri [ j ] = Normals - > AppendElement ( ( FVector3f ) VtxROINormal ) ;
2020-01-27 20:11:15 -05:00
Vertices . Add ( BaseTri [ j ] , ElemTri [ j ] ) ;
}
else
{
ElemTri [ j ] = * FoundElementID ;
}
}
Normals - > SetTriangle ( tid , ElemTri ) ;
}
}
void FDynamicMeshEditor : : SetTubeNormals ( const TArray < int > & Triangles , const TArray < int > & VertexIDs1 , const TArray < int > & MatchedIndices1 , const TArray < int > & VertexIDs2 , const TArray < int > & MatchedIndices2 )
{
check ( Mesh - > HasAttributes ( ) ) ;
check ( MatchedIndices1 . Num ( ) = = MatchedIndices2 . Num ( ) ) ;
int NumMatched = MatchedIndices1 . Num ( ) ;
FDynamicMeshNormalOverlay * Normals = Mesh - > Attributes ( ) - > PrimaryNormals ( ) ;
TArray < FVector3f > MatchedEdgeNormals [ 2 ] ; // matched edge normals for the two sides
MatchedEdgeNormals [ 0 ] . SetNum ( NumMatched ) ;
MatchedEdgeNormals [ 1 ] . SetNum ( NumMatched ) ;
for ( int LastMatchedIdx = NumMatched - 1 , Idx = 0 ; Idx < NumMatched ; LastMatchedIdx = Idx + + )
{
// get edge indices
int M1 [ 2 ] { MatchedIndices1 [ LastMatchedIdx ] , MatchedIndices1 [ Idx ] } ;
int M2 [ 2 ] { MatchedIndices2 [ LastMatchedIdx ] , MatchedIndices2 [ Idx ] } ;
// make sure they're not the same index
if ( M1 [ 0 ] = = M1 [ 1 ] )
{
M1 [ 1 ] = ( M1 [ 1 ] + 1 ) % VertexIDs1 . Num ( ) ;
}
if ( M2 [ 0 ] = = M2 [ 1 ] )
{
M2 [ 1 ] = ( M2 [ 1 ] + 1 ) % VertexIDs2 . Num ( ) ;
}
FVector3f Corners [ 4 ] { ( FVector3f ) Mesh - > GetVertex ( VertexIDs1 [ M1 [ 0 ] ] ) , ( FVector3f ) Mesh - > GetVertex ( VertexIDs1 [ M1 [ 1 ] ] ) , ( FVector3f ) Mesh - > GetVertex ( VertexIDs2 [ M2 [ 0 ] ] ) , ( FVector3f ) Mesh - > GetVertex ( VertexIDs2 [ M2 [ 1 ] ] ) } ;
FVector3f Edges [ 2 ] { Corners [ 1 ] - Corners [ 0 ] , Corners [ 3 ] - Corners [ 2 ] } ;
FVector3f Across = Corners [ 2 ] - Corners [ 0 ] ;
2021-03-17 19:32:44 -04:00
MatchedEdgeNormals [ 0 ] [ LastMatchedIdx ] = Normalized ( Edges [ 0 ] . Cross ( Across ) ) ;
MatchedEdgeNormals [ 1 ] [ LastMatchedIdx ] = Normalized ( Edges [ 1 ] . Cross ( Across ) ) ;
2020-01-27 20:11:15 -05:00
}
TArray < FVector3f > MatchedVertNormals [ 2 ] ;
MatchedVertNormals [ 0 ] . SetNum ( NumMatched ) ;
MatchedVertNormals [ 1 ] . SetNum ( NumMatched ) ;
TArray < FVector3f > VertNormals [ 2 ] ;
VertNormals [ 0 ] . SetNum ( VertexIDs1 . Num ( ) ) ;
VertNormals [ 1 ] . SetNum ( VertexIDs2 . Num ( ) ) ;
for ( int LastMatchedIdx = NumMatched - 1 , Idx = 0 ; Idx < NumMatched ; LastMatchedIdx = Idx + + )
{
2021-03-17 19:32:44 -04:00
MatchedVertNormals [ 0 ] [ Idx ] = Normalized ( MatchedEdgeNormals [ 0 ] [ LastMatchedIdx ] + MatchedEdgeNormals [ 0 ] [ Idx ] ) ;
MatchedVertNormals [ 1 ] [ Idx ] = Normalized ( MatchedEdgeNormals [ 1 ] [ LastMatchedIdx ] + MatchedEdgeNormals [ 1 ] [ Idx ] ) ;
2020-01-27 20:11:15 -05:00
}
TMap < int , int > VertToElID ;
for ( int Side = 0 ; Side < 2 ; Side + + )
{
const TArray < int > & MatchedIndices = Side = = 0 ? MatchedIndices1 : MatchedIndices2 ;
const TArray < int > & VertexIDs = Side = = 0 ? VertexIDs1 : VertexIDs2 ;
int NumVertices = VertNormals [ Side ] . Num ( ) ;
for ( int LastMatchedIdx = NumMatched - 1 , Idx = 0 ; Idx < NumMatched ; LastMatchedIdx = Idx + + )
{
int Start = MatchedIndices [ LastMatchedIdx ] , End = MatchedIndices [ Idx ] ;
VertNormals [ Side ] [ End ] = MatchedVertNormals [ Side ] [ Idx ] ;
if ( Start ! = End )
{
VertNormals [ Side ] [ Start ] = MatchedVertNormals [ Side ] [ LastMatchedIdx ] ;
FVector3d StartPos = Mesh - > GetVertex ( VertexIDs [ Start ] ) ;
FVector3d Along = Mesh - > GetVertex ( VertexIDs [ End ] ) - StartPos ;
double SepSq = Along . SquaredLength ( ) ;
if ( SepSq < KINDA_SMALL_NUMBER )
{
for ( int InsideIdx = ( Start + 1 ) % NumVertices ; InsideIdx ! = End ; InsideIdx = ( InsideIdx + 1 ) % NumVertices )
{
VertNormals [ Side ] [ InsideIdx ] = VertNormals [ Side ] [ End ] ; // just copy the end normal in since all the vertices are almost in the same position
}
}
for ( int InsideIdx = ( Start + 1 ) % NumVertices ; InsideIdx ! = End ; InsideIdx = ( InsideIdx + 1 ) % NumVertices )
{
double InterpT = ( Mesh - > GetVertex ( VertexIDs [ InsideIdx ] ) - StartPos ) . Dot ( Along ) / SepSq ;
VertNormals [ Side ] [ InsideIdx ] = InterpT * VertNormals [ Side ] [ End ] + ( 1 - InterpT ) * VertNormals [ Side ] [ Start ] ;
}
}
}
for ( int Idx = 0 ; Idx < NumVertices ; Idx + + )
{
int VID = VertexIDs [ Idx ] ;
2020-04-18 18:42:59 -04:00
VertToElID . Add ( VID , Normals - > AppendElement ( VertNormals [ Side ] [ Idx ] ) ) ;
2020-01-27 20:11:15 -05:00
}
}
for ( int TID : Triangles )
{
FIndex3i Tri = Mesh - > GetTriangle ( TID ) ;
FIndex3i ElTri ( VertToElID [ Tri . A ] , VertToElID [ Tri . B ] , VertToElID [ Tri . C ] ) ;
Normals - > SetTriangle ( TID , ElTri ) ;
}
}
void FDynamicMeshEditor : : SetGeneralTubeUVs ( const TArray < int > & Triangles ,
const TArray < int > & VertexIDs1 , const TArray < int > & MatchedIndices1 , const TArray < int > & VertexIDs2 , const TArray < int > & MatchedIndices2 ,
const TArray < float > & UValues , const FVector3f & VDir ,
float UVScaleFactor , const FVector2f & UVTranslation , int UVLayerIndex )
{
// not really a valid tube if only two vertices on either side
if ( ! ensure ( VertexIDs1 . Num ( ) > = 3 & & VertexIDs2 . Num ( ) > = 3 ) )
{
return ;
}
check ( Mesh - > HasAttributes ( ) ) ;
check ( MatchedIndices1 . Num ( ) = = MatchedIndices2 . Num ( ) ) ;
check ( UValues . Num ( ) = = MatchedIndices1 . Num ( ) + 1 ) ;
int NumMatched = MatchedIndices1 . Num ( ) ;
FDynamicMeshUVOverlay * UVs = Mesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
FVector3d RefPos = Mesh - > GetVertex ( VertexIDs1 [ 0 ] ) ;
auto GetUV = [ this , & VDir , & UVScaleFactor , & UVTranslation , & RefPos ] ( int MeshIdx , float UStart , float UEnd , float Param )
{
return FVector2f ( ( Mesh - > GetVertex ( MeshIdx ) - RefPos ) . Dot ( ( FVector3d ) VDir ) , FMath : : Lerp ( UStart , UEnd , Param ) ) * UVScaleFactor + UVTranslation ;
} ;
TArray < FVector2f > VertUVs [ 2 ] ;
VertUVs [ 0 ] . SetNum ( VertexIDs1 . Num ( ) + 1 ) ;
VertUVs [ 1 ] . SetNum ( VertexIDs2 . Num ( ) + 1 ) ;
TMap < int , int > VertToElID ;
int DuplicateMappingForLastVert [ 2 ] { - 1 , - 1 } ; // second element ids for the first vertices, to handle the seam at the loop
for ( int Side = 0 ; Side < 2 ; Side + + )
{
const TArray < int > & MatchedIndices = Side = = 0 ? MatchedIndices1 : MatchedIndices2 ;
const TArray < int > & VertexIDs = Side = = 0 ? VertexIDs1 : VertexIDs2 ;
int NumVertices = VertexIDs . Num ( ) ;
for ( int Idx = 0 ; Idx < NumMatched ; Idx + + )
{
int NextIdx = Idx + 1 ;
int NextIdxLooped = NextIdx % NumMatched ;
bool bOnLast = NextIdx = = NumMatched ;
int Start = MatchedIndices [ Idx ] , End = MatchedIndices [ NextIdxLooped ] ;
int EndUnlooped = bOnLast ? NumVertices : End ;
VertUVs [ Side ] [ EndUnlooped ] = GetUV ( VertexIDs [ End ] , UValues [ Idx ] , UValues [ NextIdx ] , 1.0f ) ;
if ( Start ! = End )
{
VertUVs [ Side ] [ Start ] = GetUV ( VertexIDs [ Start ] , UValues [ Idx ] , UValues [ NextIdx ] , 0.0f ) ;
FVector3d StartPos = Mesh - > GetVertex ( VertexIDs [ Start ] ) ;
FVector3d Along = Mesh - > GetVertex ( VertexIDs [ End ] ) - StartPos ;
double SepSq = Along . SquaredLength ( ) ;
if ( SepSq < KINDA_SMALL_NUMBER )
{
for ( int InsideIdx = ( Start + 1 ) % NumVertices ; InsideIdx ! = End ; InsideIdx = ( InsideIdx + 1 ) % NumVertices )
{
VertUVs [ Side ] [ InsideIdx ] = VertUVs [ Side ] [ EndUnlooped ] ; // just copy the end normal in since all the vertices are almost in the same position
}
}
for ( int InsideIdx = ( Start + 1 ) % NumVertices ; InsideIdx ! = End ; InsideIdx = ( InsideIdx + 1 ) % NumVertices )
{
double InterpT = ( Mesh - > GetVertex ( VertexIDs [ InsideIdx ] ) - StartPos ) . Dot ( Along ) / SepSq ;
VertUVs [ Side ] [ InsideIdx ] = GetUV ( VertexIDs [ InsideIdx ] , UValues [ Idx ] , UValues [ NextIdx ] , InterpT ) ;
}
}
}
for ( int Idx = 0 ; Idx < NumVertices ; Idx + + )
{
int VID = VertexIDs [ Idx ] ;
2020-04-18 18:42:59 -04:00
VertToElID . Add ( VID , UVs - > AppendElement ( VertUVs [ Side ] [ Idx ] ) ) ;
2020-01-27 20:11:15 -05:00
}
2020-04-18 18:42:59 -04:00
DuplicateMappingForLastVert [ Side ] = UVs - > AppendElement ( VertUVs [ Side ] . Last ( ) ) ;
2020-01-27 20:11:15 -05:00
}
bool bPastInitialVertices [ 2 ] { false , false } ;
int FirstVID [ 2 ] = { VertexIDs1 [ 0 ] , VertexIDs2 [ 0 ] } ;
for ( int TriIdx = 0 ; TriIdx < Triangles . Num ( ) ; TriIdx + + )
{
int TID = Triangles [ TriIdx ] ;
FIndex3i Tri = Mesh - > GetTriangle ( TID ) ;
FIndex3i ElTri ( VertToElID [ Tri . A ] , VertToElID [ Tri . B ] , VertToElID [ Tri . C ] ) ;
// hacky special handling for the seam at the end of the loop -- the second time we see the start vertices, switch to the end seam elements
for ( int Side = 0 ; Side < 2 ; Side + + )
{
int FirstVIDSubIdx = Tri . IndexOf ( FirstVID [ Side ] ) ;
bPastInitialVertices [ Side ] = bPastInitialVertices [ Side ] | | FirstVIDSubIdx = = - 1 ;
if ( bPastInitialVertices [ Side ] & & FirstVIDSubIdx > = 0 )
{
ElTri [ FirstVIDSubIdx ] = DuplicateMappingForLastVert [ Side ] ;
}
}
UVs - > SetTriangle ( TID , ElTri ) ;
}
}
void FDynamicMeshEditor : : SetTriangleUVsFromProjection ( const TArray < int > & Triangles , const FFrame3d & ProjectionFrame , float UVScaleFactor , const FVector2f & UVTranslation , bool bShiftToOrigin , int UVLayerIndex )
2020-08-11 01:36:57 -04:00
{
SetTriangleUVsFromProjection ( Triangles , ProjectionFrame , FVector2f ( UVScaleFactor , UVScaleFactor ) , UVTranslation , UVLayerIndex , bShiftToOrigin , false ) ;
}
void FDynamicMeshEditor : : SetTriangleUVsFromProjection ( const TArray < int > & Triangles , const FFrame3d & ProjectionFrame , const FVector2f & UVScale ,
const FVector2f & UVTranslation , int UVLayerIndex , bool bShiftToOrigin , bool bNormalizeBeforeScaling )
2019-09-10 11:35:20 -04:00
{
if ( ! Triangles . Num ( ) )
{
return ;
}
check ( Mesh - > HasAttributes ( ) & & Mesh - > Attributes ( ) - > NumUVLayers ( ) > UVLayerIndex ) ;
FDynamicMeshUVOverlay * UVs = Mesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
TMap < int , int > BaseToOverlayVIDMap ;
TArray < int > AllUVIndices ;
FAxisAlignedBox2f UVBounds ( FAxisAlignedBox2f : : Empty ( ) ) ;
for ( int TID : Triangles )
{
2020-08-11 01:36:57 -04:00
if ( UVs - > IsSetTriangle ( TID ) )
{
UVs - > UnsetTriangle ( TID ) ;
}
2019-09-10 11:35:20 -04:00
FIndex3i BaseTri = Mesh - > GetTriangle ( TID ) ;
FIndex3i ElemTri ;
for ( int j = 0 ; j < 3 ; + + j )
{
const int * FoundElementID = BaseToOverlayVIDMap . Find ( BaseTri [ j ] ) ;
if ( FoundElementID = = nullptr )
{
FVector2f UV = ( FVector2f ) ProjectionFrame . ToPlaneUV ( Mesh - > GetVertex ( BaseTri [ j ] ) , 2 ) ;
UVBounds . Contain ( UV ) ;
2020-04-18 18:42:59 -04:00
ElemTri [ j ] = UVs - > AppendElement ( UV ) ;
2019-09-10 11:35:20 -04:00
AllUVIndices . Add ( ElemTri [ j ] ) ;
BaseToOverlayVIDMap . Add ( BaseTri [ j ] , ElemTri [ j ] ) ;
}
else
{
ElemTri [ j ] = * FoundElementID ;
}
}
UVs - > SetTriangle ( TID , ElemTri ) ;
}
2020-08-11 01:36:57 -04:00
FVector2f UvScaleToUse = bNormalizeBeforeScaling ? FVector2f ( UVScale [ 0 ] / UVBounds . Width ( ) , UVScale [ 1 ] / UVBounds . Height ( ) )
: UVScale ;
2019-09-10 11:35:20 -04:00
// shift UVs so that their bbox min-corner is at origin and scaled by external scale factor
for ( int UVID : AllUVIndices )
{
FVector2f UV = UVs - > GetElement ( UVID ) ;
2020-08-11 01:36:57 -04:00
FVector2f TransformedUV = ( bShiftToOrigin ) ? ( ( UV - UVBounds . Min ) * UvScaleToUse ) : ( UV * UvScaleToUse ) ;
2019-10-01 20:41:42 -04:00
TransformedUV + = UVTranslation ;
2019-09-10 11:35:20 -04:00
UVs - > SetElement ( UVID , TransformedUV ) ;
}
}
2019-06-04 15:42:48 -04:00
2019-10-01 20:41:42 -04:00
void FDynamicMeshEditor : : SetQuadUVsFromProjection ( const FIndex2i & QuadTris , const FFrame3d & ProjectionFrame , float UVScaleFactor , const FVector2f & UVTranslation , int UVLayerIndex )
2019-06-04 15:42:48 -04:00
{
check ( Mesh - > HasAttributes ( ) & & Mesh - > Attributes ( ) - > NumUVLayers ( ) > UVLayerIndex ) ;
FDynamicMeshUVOverlay * UVs = Mesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
FIndex4i AllUVIndices ( - 1 , - 1 , - 1 , - 1 ) ;
FVector2f AllUVs [ 4 ] ;
// project first triangle
FIndex3i Triangle1 = Mesh - > GetTriangle ( QuadTris . A ) ;
FIndex3i UVTriangle1 ;
for ( int j = 0 ; j < 3 ; + + j )
{
2019-09-10 11:35:20 -04:00
FVector2f UV = ( FVector2f ) ProjectionFrame . ToPlaneUV ( Mesh - > GetVertex ( Triangle1 [ j ] ) , 2 ) ;
2020-04-18 18:42:59 -04:00
UVTriangle1 [ j ] = UVs - > AppendElement ( UV ) ;
2019-06-04 15:42:48 -04:00
AllUVs [ j ] = UV ;
AllUVIndices [ j ] = UVTriangle1 [ j ] ;
}
UVs - > SetTriangle ( QuadTris . A , UVTriangle1 ) ;
// project second triangle
if ( Mesh - > IsTriangle ( QuadTris . B ) )
{
FIndex3i Triangle2 = Mesh - > GetTriangle ( QuadTris . B ) ;
FIndex3i UVTriangle2 ;
for ( int j = 0 ; j < 3 ; + + j )
{
int i = Triangle1 . IndexOf ( Triangle2 [ j ] ) ;
if ( i = = - 1 )
{
2019-09-10 11:35:20 -04:00
FVector2f UV = ( FVector2f ) ProjectionFrame . ToPlaneUV ( Mesh - > GetVertex ( Triangle2 [ j ] ) , 2 ) ;
2020-04-18 18:42:59 -04:00
UVTriangle2 [ j ] = UVs - > AppendElement ( UV ) ;
2019-06-04 15:42:48 -04:00
AllUVs [ 3 ] = UV ;
AllUVIndices [ 3 ] = UVTriangle2 [ j ] ;
}
else
{
UVTriangle2 [ j ] = UVTriangle1 [ i ] ;
}
}
UVs - > SetTriangle ( QuadTris . B , UVTriangle2 ) ;
}
// shift UVs so that their bbox min-corner is at origin and scaled by external scale factor
FAxisAlignedBox2f UVBounds ( FAxisAlignedBox2f : : Empty ( ) ) ;
UVBounds . Contain ( AllUVs [ 0 ] ) ; UVBounds . Contain ( AllUVs [ 1 ] ) ; UVBounds . Contain ( AllUVs [ 2 ] ) ;
if ( AllUVIndices [ 3 ] ! = - 1 )
{
UVBounds . Contain ( AllUVs [ 3 ] ) ;
}
for ( int j = 0 ; j < 4 ; + + j )
{
if ( AllUVIndices [ j ] ! = - 1 )
{
FVector2f TransformedUV = ( AllUVs [ j ] - UVBounds . Min ) * UVScaleFactor ;
2019-10-01 20:41:42 -04:00
TransformedUV + = UVTranslation ;
2019-06-04 15:42:48 -04:00
UVs - > SetElement ( AllUVIndices [ j ] , TransformedUV ) ;
}
}
}
2022-01-29 14:37:53 -05:00
void FDynamicMeshEditor : : RescaleAttributeUVs ( float UVScale , bool bWorldSpace , int UVLayerIndex , TOptional < FTransformSRT3d > ToWorld )
2019-10-01 20:41:42 -04:00
{
check ( Mesh - > HasAttributes ( ) & & Mesh - > Attributes ( ) - > NumUVLayers ( ) > UVLayerIndex ) ;
FDynamicMeshUVOverlay * UVs = Mesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
if ( bWorldSpace )
{
FVector2f TriUVs [ 3 ] ;
FVector3d TriVs [ 3 ] ;
float TotalEdgeUVLen = 0 ;
double TotalEdgeLen = 0 ;
for ( int TID : Mesh - > TriangleIndicesItr ( ) )
{
2022-03-11 12:13:47 -05:00
if ( ! UVs - > IsSetTriangle ( TID ) )
{
continue ;
}
2019-10-01 20:41:42 -04:00
UVs - > GetTriElements ( TID , TriUVs [ 0 ] , TriUVs [ 1 ] , TriUVs [ 2 ] ) ;
Mesh - > GetTriVertices ( TID , TriVs [ 0 ] , TriVs [ 1 ] , TriVs [ 2 ] ) ;
if ( ToWorld . IsSet ( ) )
{
for ( int i = 0 ; i < 3 ; i + + )
{
TriVs [ i ] = ToWorld - > TransformPosition ( TriVs [ i ] ) ;
}
}
for ( int j = 2 , i = 0 ; i < 3 ; j = i + + )
{
2021-08-30 18:03:07 -04:00
TotalEdgeUVLen + = Distance ( TriUVs [ j ] , TriUVs [ i ] ) ;
2021-03-30 21:25:22 -04:00
TotalEdgeLen + = Distance ( TriVs [ j ] , TriVs [ i ] ) ;
2019-10-01 20:41:42 -04:00
}
}
if ( TotalEdgeUVLen > KINDA_SMALL_NUMBER )
{
float AvgUVScale = TotalEdgeLen / TotalEdgeUVLen ;
UVScale * = AvgUVScale ;
}
}
for ( int UVID : UVs - > ElementIndicesItr ( ) )
{
FVector2f UV ;
UVs - > GetElement ( UVID , UV ) ;
UVs - > SetElement ( UVID , UV * UVScale ) ;
}
}
2019-06-04 15:42:48 -04:00
void FDynamicMeshEditor : : ReverseTriangleOrientations ( const TArray < int > & Triangles , bool bInvertNormals )
{
for ( int tid : Triangles )
{
Mesh - > ReverseTriOrientation ( tid ) ;
}
if ( bInvertNormals )
{
InvertTriangleNormals ( Triangles ) ;
}
}
void FDynamicMeshEditor : : InvertTriangleNormals ( const TArray < int > & Triangles )
{
// @todo re-use the TBitA
if ( Mesh - > HasVertexNormals ( ) )
{
TBitArray < FDefaultBitArrayAllocator > DoneVertices ( false , Mesh - > MaxVertexID ( ) ) ;
for ( int TriangleID : Triangles )
{
FIndex3i Tri = Mesh - > GetTriangle ( TriangleID ) ;
for ( int j = 0 ; j < 3 ; + + j )
{
if ( DoneVertices [ Tri [ j ] ] = = false )
{
Mesh - > SetVertexNormal ( Tri [ j ] , - Mesh - > GetVertexNormal ( Tri [ j ] ) ) ;
DoneVertices [ Tri [ j ] ] = true ;
}
}
}
}
if ( Mesh - > HasAttributes ( ) )
{
2021-01-11 12:43:33 -04:00
for ( int NormalLayerIndex = 0 ; NormalLayerIndex < Mesh - > Attributes ( ) - > NumNormalLayers ( ) ; NormalLayerIndex + + )
2019-06-04 15:42:48 -04:00
{
2021-01-11 12:43:33 -04:00
FDynamicMeshNormalOverlay * NormalOverlay = Mesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
TBitArray < FDefaultBitArrayAllocator > DoneNormals ( false , NormalOverlay - > MaxElementID ( ) ) ;
2019-06-04 15:42:48 -04:00
for ( int TriangleID : Triangles )
{
2021-01-11 12:43:33 -04:00
FIndex3i ElemTri = NormalOverlay - > GetTriangle ( TriangleID ) ;
2019-06-04 15:42:48 -04:00
for ( int j = 0 ; j < 3 ; + + j )
{
2021-01-11 12:43:33 -04:00
if ( NormalOverlay - > IsElement ( ElemTri [ j ] ) & & DoneNormals [ ElemTri [ j ] ] = = false )
2019-06-04 15:42:48 -04:00
{
2021-01-11 12:43:33 -04:00
NormalOverlay - > SetElement ( ElemTri [ j ] , - NormalOverlay - > GetElement ( ElemTri [ j ] ) ) ;
2019-06-04 15:42:48 -04:00
DoneNormals [ ElemTri [ j ] ] = true ;
}
}
}
}
}
}
void FDynamicMeshEditor : : CopyAttributes ( int FromTriangleID , int ToTriangleID , FMeshIndexMappings & IndexMaps , FDynamicMeshEditResult & ResultOut )
{
if ( Mesh - > HasAttributes ( ) = = false )
{
return ;
}
2019-10-01 20:41:42 -04:00
for ( int UVLayerIndex = 0 ; UVLayerIndex < Mesh - > Attributes ( ) - > NumUVLayers ( ) ; UVLayerIndex + + )
2019-06-04 15:42:48 -04:00
{
2019-10-01 20:41:42 -04:00
FDynamicMeshUVOverlay * UVOverlay = Mesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
2020-01-27 20:11:15 -05:00
if ( UVOverlay - > IsSetTriangle ( FromTriangleID ) )
2019-06-04 15:42:48 -04:00
{
2020-01-27 20:11:15 -05:00
FIndex3i FromElemTri = UVOverlay - > GetTriangle ( FromTriangleID ) ;
FIndex3i ToElemTri = UVOverlay - > GetTriangle ( ToTriangleID ) ;
for ( int j = 0 ; j < 3 ; + + j )
2019-06-04 15:42:48 -04:00
{
int NewElemID = FindOrCreateDuplicateUV ( FromElemTri [ j ] , UVLayerIndex , IndexMaps ) ;
ToElemTri [ j ] = NewElemID ;
}
2020-01-27 20:11:15 -05:00
UVOverlay - > SetTriangle ( ToTriangleID , ToElemTri ) ;
2019-06-04 15:42:48 -04:00
}
}
2020-06-23 18:40:00 -04:00
// Make sure the storage in NewNormalOverlayElements has a slot for each normal layer.
if ( ResultOut . NewNormalOverlayElements . Num ( ) < Mesh - > Attributes ( ) - > NumNormalLayers ( ) )
{
ResultOut . NewNormalOverlayElements . AddDefaulted ( Mesh - > Attributes ( ) - > NumNormalLayers ( ) - ResultOut . NewNormalOverlayElements . Num ( ) ) ;
}
2019-06-04 15:42:48 -04:00
2020-01-27 20:11:15 -05:00
for ( int NormalLayerIndex = 0 ; NormalLayerIndex < Mesh - > Attributes ( ) - > NumNormalLayers ( ) ; NormalLayerIndex + + )
2019-06-04 15:42:48 -04:00
{
2020-01-27 20:11:15 -05:00
FDynamicMeshNormalOverlay * NormalOverlay = Mesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
2020-06-23 18:40:00 -04:00
2020-01-27 20:11:15 -05:00
if ( NormalOverlay - > IsSetTriangle ( FromTriangleID ) )
2019-06-04 15:42:48 -04:00
{
2020-01-27 20:11:15 -05:00
FIndex3i FromElemTri = NormalOverlay - > GetTriangle ( FromTriangleID ) ;
FIndex3i ToElemTri = NormalOverlay - > GetTriangle ( ToTriangleID ) ;
for ( int j = 0 ; j < 3 ; + + j )
2019-06-04 15:42:48 -04:00
{
2020-06-23 18:40:00 -04:00
int NewElemID = FindOrCreateDuplicateNormal ( FromElemTri [ j ] , NormalLayerIndex , IndexMaps , & ResultOut ) ;
2019-06-04 15:42:48 -04:00
ToElemTri [ j ] = NewElemID ;
}
2020-01-27 20:11:15 -05:00
NormalOverlay - > SetTriangle ( ToTriangleID , ToElemTri ) ;
2019-06-04 15:42:48 -04:00
}
}
2021-04-19 10:47:52 -04:00
if ( Mesh - > Attributes ( ) - > HasPrimaryColors ( ) )
{
FDynamicMeshColorOverlay * ColorOverlay = Mesh - > Attributes ( ) - > PrimaryColors ( ) ;
if ( ColorOverlay - > IsSetTriangle ( FromTriangleID ) )
{
FIndex3i FromElemTri = ColorOverlay - > GetTriangle ( FromTriangleID ) ;
FIndex3i ToElemTri = ColorOverlay - > GetTriangle ( ToTriangleID ) ;
for ( int j = 0 ; j < 3 ; + + j )
{
int NewElemID = FindOrCreateDuplicateColor ( FromElemTri [ j ] , IndexMaps , & ResultOut ) ;
ToElemTri [ j ] = NewElemID ;
}
ColorOverlay - > SetTriangle ( ToTriangleID , ToElemTri ) ;
}
}
2019-12-19 18:07:47 -05:00
if ( Mesh - > Attributes ( ) - > HasMaterialID ( ) )
{
FDynamicMeshMaterialAttribute * MaterialIDs = Mesh - > Attributes ( ) - > GetMaterialID ( ) ;
MaterialIDs - > SetValue ( ToTriangleID , MaterialIDs - > GetValue ( FromTriangleID ) ) ;
}
2020-11-26 20:12:25 -04:00
for ( int PolygroupLayerIndex = 0 ; PolygroupLayerIndex < Mesh - > Attributes ( ) - > NumPolygroupLayers ( ) ; PolygroupLayerIndex + + )
{
FDynamicMeshPolygroupAttribute * Polygroup = Mesh - > Attributes ( ) - > GetPolygroupLayer ( PolygroupLayerIndex ) ;
Polygroup - > SetValue ( ToTriangleID , Polygroup - > GetValue ( FromTriangleID ) ) ;
}
2019-06-04 15:42:48 -04:00
}
int FDynamicMeshEditor : : FindOrCreateDuplicateUV ( int ElementID , int UVLayerIndex , FMeshIndexMappings & IndexMaps )
{
int NewElementID = IndexMaps . GetNewUV ( UVLayerIndex , ElementID ) ;
if ( NewElementID = = IndexMaps . InvalidID ( ) )
{
FDynamicMeshUVOverlay * UVOverlay = Mesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
2020-04-18 18:42:59 -04:00
NewElementID = UVOverlay - > AppendElement ( UVOverlay - > GetElement ( ElementID ) ) ;
2019-06-04 15:42:48 -04:00
IndexMaps . SetUV ( UVLayerIndex , ElementID , NewElementID ) ;
}
return NewElementID ;
}
2020-06-23 18:40:00 -04:00
int FDynamicMeshEditor : : FindOrCreateDuplicateNormal ( int ElementID , int NormalLayerIndex , FMeshIndexMappings & IndexMaps , FDynamicMeshEditResult * ResultOut )
2019-06-04 15:42:48 -04:00
{
int NewElementID = IndexMaps . GetNewNormal ( NormalLayerIndex , ElementID ) ;
if ( NewElementID = = IndexMaps . InvalidID ( ) )
{
FDynamicMeshNormalOverlay * NormalOverlay = Mesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
2020-04-18 18:42:59 -04:00
NewElementID = NormalOverlay - > AppendElement ( NormalOverlay - > GetElement ( ElementID ) ) ;
2019-06-04 15:42:48 -04:00
IndexMaps . SetNormal ( NormalLayerIndex , ElementID , NewElementID ) ;
2020-06-23 18:40:00 -04:00
if ( ResultOut )
{
check ( ResultOut - > NewNormalOverlayElements . Num ( ) > NormalLayerIndex ) ;
ResultOut - > NewNormalOverlayElements [ NormalLayerIndex ] . Add ( NewElementID ) ;
}
2019-06-04 15:42:48 -04:00
}
return NewElementID ;
}
2021-04-19 10:47:52 -04:00
int FDynamicMeshEditor : : FindOrCreateDuplicateColor ( int ElementID , FMeshIndexMappings & IndexMaps , FDynamicMeshEditResult * ResultOut )
{
int NewElementID = IndexMaps . GetNewColor ( ElementID ) ;
if ( NewElementID = = IndexMaps . InvalidID ( ) )
{
FDynamicMeshColorOverlay * ColorOverlay = Mesh - > Attributes ( ) - > PrimaryColors ( ) ;
NewElementID = ColorOverlay - > AppendElement ( ColorOverlay - > GetElement ( ElementID ) ) ;
IndexMaps . SetColor ( ElementID , NewElementID ) ;
if ( ResultOut )
{
ResultOut - > NewColorOverlayElements . Add ( NewElementID ) ;
}
}
return NewElementID ;
}
2019-06-04 15:42:48 -04:00
int FDynamicMeshEditor : : FindOrCreateDuplicateVertex ( int VertexID , FMeshIndexMappings & IndexMaps , FDynamicMeshEditResult & ResultOut )
{
int NewVertexID = IndexMaps . GetNewVertex ( VertexID ) ;
if ( NewVertexID = = IndexMaps . InvalidID ( ) )
{
NewVertexID = Mesh - > AppendVertex ( * Mesh , VertexID ) ;
IndexMaps . SetVertex ( VertexID , NewVertexID ) ;
ResultOut . NewVertices . Add ( NewVertexID ) ;
2022-03-29 20:04:24 -04:00
if ( Mesh - > HasAttributes ( ) )
{
for ( int WeightLayerIndex = 0 ; WeightLayerIndex < Mesh - > Attributes ( ) - > NumWeightLayers ( ) ; + + WeightLayerIndex )
{
FDynamicMeshWeightAttribute * WeightAttr = Mesh - > Attributes ( ) - > GetWeightLayer ( WeightLayerIndex ) ;
float Val ;
WeightAttr - > GetValue ( VertexID , & Val ) ;
WeightAttr - > SetNewValue ( NewVertexID , & Val ) ;
}
}
2019-06-04 15:42:48 -04:00
}
return NewVertexID ;
}
int FDynamicMeshEditor : : FindOrCreateDuplicateGroup ( int TriangleID , FMeshIndexMappings & IndexMaps , FDynamicMeshEditResult & ResultOut )
{
int GroupID = Mesh - > GetTriangleGroup ( TriangleID ) ;
int NewGroupID = IndexMaps . GetNewGroup ( GroupID ) ;
if ( NewGroupID = = IndexMaps . InvalidID ( ) )
{
NewGroupID = Mesh - > AllocateTriangleGroup ( ) ;
IndexMaps . SetGroup ( GroupID , NewGroupID ) ;
ResultOut . NewGroups . Add ( NewGroupID ) ;
}
return NewGroupID ;
}
void FDynamicMeshEditor : : AppendMesh ( const FDynamicMesh3 * AppendMesh ,
2019-09-10 11:35:20 -04:00
FMeshIndexMappings & IndexMapsOut ,
2019-06-04 15:42:48 -04:00
TFunction < FVector3d ( int , const FVector3d & ) > PositionTransform ,
2019-09-10 11:35:20 -04:00
TFunction < FVector3d ( int , const FVector3d & ) > NormalTransform )
2019-06-04 15:42:48 -04:00
{
2019-11-15 10:21:17 -05:00
// todo: handle this case by making a copy?
check ( AppendMesh ! = Mesh ) ;
2019-06-04 15:42:48 -04:00
IndexMapsOut . Reset ( ) ;
2019-09-10 11:35:20 -04:00
IndexMapsOut . Initialize ( Mesh ) ;
2019-06-04 15:42:48 -04:00
FIndexMapi & VertexMap = IndexMapsOut . GetVertexMap ( ) ;
VertexMap . Reserve ( AppendMesh - > VertexCount ( ) ) ;
for ( int VertID : AppendMesh - > VertexIndicesItr ( ) )
{
FVector3d Position = AppendMesh - > GetVertex ( VertID ) ;
if ( PositionTransform ! = nullptr )
{
Position = PositionTransform ( VertID , Position ) ;
}
int NewVertID = Mesh - > AppendVertex ( Position ) ;
VertexMap . Add ( VertID , NewVertID ) ;
if ( AppendMesh - > HasVertexNormals ( ) & & Mesh - > HasVertexNormals ( ) )
{
FVector3f Normal = AppendMesh - > GetVertexNormal ( VertID ) ;
if ( NormalTransform ! = nullptr )
{
2019-09-10 11:35:20 -04:00
Normal = ( FVector3f ) NormalTransform ( VertID , ( FVector3d ) Normal ) ;
2019-06-04 15:42:48 -04:00
}
Mesh - > SetVertexNormal ( NewVertID , Normal ) ;
}
2020-11-16 13:32:59 -04:00
if ( AppendMesh - > HasVertexUVs ( ) & & Mesh - > HasVertexUVs ( ) )
{
FVector2f UV = AppendMesh - > GetVertexUV ( VertID ) ;
Mesh - > SetVertexUV ( NewVertID , UV ) ;
}
2019-09-10 11:35:20 -04:00
if ( AppendMesh - > HasVertexColors ( ) & & Mesh - > HasVertexColors ( ) )
2019-06-04 15:42:48 -04:00
{
FVector3f Color = AppendMesh - > GetVertexColor ( VertID ) ;
Mesh - > SetVertexColor ( NewVertID , Color ) ;
}
}
2019-09-10 11:35:20 -04:00
FIndexMapi & TriangleMap = IndexMapsOut . GetTriangleMap ( ) ;
bool bAppendGroups = AppendMesh - > HasTriangleGroups ( ) & & Mesh - > HasTriangleGroups ( ) ;
FIndexMapi & GroupsMap = IndexMapsOut . GetGroupMap ( ) ;
2019-06-04 15:42:48 -04:00
for ( int TriID : AppendMesh - > TriangleIndicesItr ( ) )
{
2019-09-10 11:35:20 -04:00
// append trigroup
int GroupID = FDynamicMesh3 : : InvalidID ;
if ( bAppendGroups )
{
GroupID = AppendMesh - > GetTriangleGroup ( TriID ) ;
if ( GroupID ! = FDynamicMesh3 : : InvalidID )
{
const int * FoundNewGroupID = GroupsMap . FindTo ( GroupID ) ;
if ( FoundNewGroupID = = nullptr )
{
int NewGroupID = Mesh - > AllocateTriangleGroup ( ) ;
GroupsMap . Add ( GroupID , NewGroupID ) ;
GroupID = NewGroupID ;
}
else
{
GroupID = * FoundNewGroupID ;
}
}
}
2019-06-04 15:42:48 -04:00
FIndex3i Tri = AppendMesh - > GetTriangle ( TriID ) ;
2019-09-10 11:35:20 -04:00
int NewTriID = Mesh - > AppendTriangle ( VertexMap . GetTo ( Tri . A ) , VertexMap . GetTo ( Tri . B ) , VertexMap . GetTo ( Tri . C ) , GroupID ) ;
2021-10-07 02:47:15 -04:00
if ( ensure ( NewTriID > = 0 ) )
{
TriangleMap . Add ( TriID , NewTriID ) ;
}
2019-06-04 15:42:48 -04:00
}
2019-09-10 11:35:20 -04:00
// @todo can we have a template fn that does this?
if ( AppendMesh - > HasAttributes ( ) & & Mesh - > HasAttributes ( ) )
{
2021-01-11 12:43:33 -04:00
int NumNormalLayers = FMath : : Min ( Mesh - > Attributes ( ) - > NumNormalLayers ( ) , AppendMesh - > Attributes ( ) - > NumNormalLayers ( ) ) ;
for ( int NormalLayerIndex = 0 ; NormalLayerIndex < NumNormalLayers ; NormalLayerIndex + + )
{
const FDynamicMeshNormalOverlay * FromNormals = AppendMesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
FDynamicMeshNormalOverlay * ToNormals = Mesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
if ( FromNormals ! = nullptr & & ToNormals ! = nullptr )
{
2021-06-22 19:35:30 -04:00
FIndexMapi & NormalMap = IndexMapsOut . GetNormalMap ( NormalLayerIndex ) ;
2021-01-11 12:43:33 -04:00
NormalMap . Reserve ( FromNormals - > ElementCount ( ) ) ;
AppendNormals ( AppendMesh , FromNormals , ToNormals ,
VertexMap , TriangleMap , NormalTransform , NormalMap ) ;
}
2019-09-10 11:35:20 -04:00
}
2019-10-01 20:41:42 -04:00
int NumUVLayers = FMath : : Min ( Mesh - > Attributes ( ) - > NumUVLayers ( ) , AppendMesh - > Attributes ( ) - > NumUVLayers ( ) ) ;
for ( int UVLayerIndex = 0 ; UVLayerIndex < NumUVLayers ; UVLayerIndex + + )
2019-09-10 11:35:20 -04:00
{
2019-10-01 20:41:42 -04:00
const FDynamicMeshUVOverlay * FromUVs = AppendMesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
FDynamicMeshUVOverlay * ToUVs = Mesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
if ( FromUVs ! = nullptr & & ToUVs ! = nullptr )
{
2019-12-19 18:07:47 -05:00
FIndexMapi & UVMap = IndexMapsOut . GetUVMap ( UVLayerIndex ) ;
2019-10-01 20:41:42 -04:00
UVMap . Reserve ( FromUVs - > ElementCount ( ) ) ;
AppendUVs ( AppendMesh , FromUVs , ToUVs ,
VertexMap , TriangleMap , UVMap ) ;
}
2019-09-10 11:35:20 -04:00
}
2019-12-19 18:07:47 -05:00
2021-04-19 10:47:52 -04:00
if ( AppendMesh - > Attributes ( ) - > HasPrimaryColors ( ) & & Mesh - > Attributes ( ) - > HasPrimaryColors ( ) )
{
const FDynamicMeshColorOverlay * FromColors = AppendMesh - > Attributes ( ) - > PrimaryColors ( ) ;
FDynamicMeshColorOverlay * ToColors = Mesh - > Attributes ( ) - > PrimaryColors ( ) ;
if ( FromColors ! = nullptr & & ToColors ! = nullptr )
{
FIndexMapi & ColorMap = IndexMapsOut . GetColorMap ( ) ;
ColorMap . Reserve ( FromColors - > ElementCount ( ) ) ;
AppendColors ( AppendMesh , FromColors , ToColors ,
VertexMap , TriangleMap , ColorMap ) ;
}
}
2019-12-19 18:07:47 -05:00
if ( AppendMesh - > Attributes ( ) - > HasMaterialID ( ) & & Mesh - > Attributes ( ) - > HasMaterialID ( ) )
{
const FDynamicMeshMaterialAttribute * FromMaterialIDs = AppendMesh - > Attributes ( ) - > GetMaterialID ( ) ;
FDynamicMeshMaterialAttribute * ToMaterialIDs = Mesh - > Attributes ( ) - > GetMaterialID ( ) ;
2021-10-07 02:47:15 -04:00
for ( const TPair < int32 , int32 > & MapTID : TriangleMap . GetForwardMap ( ) )
2019-12-19 18:07:47 -05:00
{
2021-10-07 02:47:15 -04:00
ToMaterialIDs - > SetValue ( MapTID . Value , FromMaterialIDs - > GetValue ( MapTID . Key ) ) ;
2019-12-19 18:07:47 -05:00
}
}
2020-11-01 22:12:11 -04:00
2020-11-26 20:12:25 -04:00
int NumPolygroupLayers = FMath : : Min ( Mesh - > Attributes ( ) - > NumPolygroupLayers ( ) , AppendMesh - > Attributes ( ) - > NumPolygroupLayers ( ) ) ;
for ( int PolygroupLayerIndex = 0 ; PolygroupLayerIndex < NumPolygroupLayers ; PolygroupLayerIndex + + )
{
// TODO: remap groups? this will be somewhat expensive...
const FDynamicMeshPolygroupAttribute * FromPolygroups = AppendMesh - > Attributes ( ) - > GetPolygroupLayer ( PolygroupLayerIndex ) ;
FDynamicMeshPolygroupAttribute * ToPolygroups = Mesh - > Attributes ( ) - > GetPolygroupLayer ( PolygroupLayerIndex ) ;
2021-10-07 02:47:15 -04:00
for ( const TPair < int32 , int32 > & MapTID : TriangleMap . GetForwardMap ( ) )
2020-11-26 20:12:25 -04:00
{
2021-10-07 02:47:15 -04:00
ToPolygroups - > SetValue ( MapTID . Value , FromPolygroups - > GetValue ( MapTID . Key ) ) ;
2020-11-26 20:12:25 -04:00
}
}
2022-03-29 20:04:24 -04:00
int NumWeightLayers = FMath : : Min ( Mesh - > Attributes ( ) - > NumWeightLayers ( ) , AppendMesh - > Attributes ( ) - > NumWeightLayers ( ) ) ;
for ( int WeightLayerIndex = 0 ; WeightLayerIndex < NumWeightLayers ; WeightLayerIndex + + )
{
const FDynamicMeshWeightAttribute * FromWeights = AppendMesh - > Attributes ( ) - > GetWeightLayer ( WeightLayerIndex ) ;
FDynamicMeshWeightAttribute * ToWeights = Mesh - > Attributes ( ) - > GetWeightLayer ( WeightLayerIndex ) ;
for ( const TPair < int32 , int32 > & MapVID : VertexMap . GetForwardMap ( ) )
{
float Weight ;
FromWeights - > GetValue ( MapVID . Key , & Weight ) ;
ToWeights - > SetValue ( MapVID . Value , & Weight ) ;
}
}
2021-05-04 23:49:23 -04:00
for ( const TPair < FName , TUniquePtr < FDynamicMeshVertexSkinWeightsAttribute > > & AttribPair : AppendMesh - > Attributes ( ) - > GetSkinWeightsAttributes ( ) )
{
FDynamicMeshVertexSkinWeightsAttribute * ToAttrib = Mesh - > Attributes ( ) - > GetSkinWeightsAttribute ( AttribPair . Key ) ;
if ( ToAttrib )
{
ToAttrib - > CopyThroughMapping ( AttribPair . Value . Get ( ) , IndexMapsOut ) ;
}
}
2020-11-01 22:12:11 -04:00
for ( const TPair < FName , TUniquePtr < FDynamicMeshAttributeBase > > & AttribPair : AppendMesh - > Attributes ( ) - > GetAttachedAttributes ( ) )
{
if ( Mesh - > Attributes ( ) - > HasAttachedAttribute ( AttribPair . Key ) )
{
FDynamicMeshAttributeBase * ToAttrib = Mesh - > Attributes ( ) - > GetAttachedAttribute ( AttribPair . Key ) ;
ToAttrib - > CopyThroughMapping ( AttribPair . Value . Get ( ) , IndexMapsOut ) ;
}
}
2019-09-10 11:35:20 -04:00
}
}
2021-05-11 16:32:22 -04:00
void FDynamicMeshEditor : : AppendMesh ( const TTriangleMeshAdapter < double > * AppendMesh ,
FMeshIndexMappings & IndexMapsOut ,
TFunction < FVector3d ( int , const FVector3d & ) > PositionTransform )
{
IndexMapsOut . Reset ( ) ;
//IndexMapsOut.Initialize(Mesh); // not supported
FIndexMapi & VertexMap = IndexMapsOut . GetVertexMap ( ) ;
VertexMap . Reserve ( AppendMesh - > VertexCount ( ) ) ;
int32 MaxVertexID = AppendMesh - > MaxVertexID ( ) ;
for ( int32 VertID = 0 ; VertID < MaxVertexID ; + + VertID )
{
if ( AppendMesh - > IsVertex ( VertID ) = = false ) continue ;
FVector3d Position = AppendMesh - > GetVertex ( VertID ) ;
if ( PositionTransform ! = nullptr )
{
Position = PositionTransform ( VertID , Position ) ;
}
int NewVertID = Mesh - > AppendVertex ( Position ) ;
VertexMap . Add ( VertID , NewVertID ) ;
}
// set face normals if they exist
FDynamicMeshNormalOverlay * SetNormals = Mesh - > HasAttributes ( ) ? Mesh - > Attributes ( ) - > PrimaryNormals ( ) : nullptr ;
FIndexMapi & TriangleMap = IndexMapsOut . GetTriangleMap ( ) ;
int32 MaxTriangleID = AppendMesh - > MaxTriangleID ( ) ;
for ( int TriID = 0 ; TriID < MaxTriangleID ; + + TriID )
{
if ( AppendMesh - > IsTriangle ( TriID ) = = false ) continue ;
int GroupID = FDynamicMesh3 : : InvalidID ;
FIndex3i Tri = AppendMesh - > GetTriangle ( TriID ) ;
FIndex3i NewTri = FIndex3i ( VertexMap . GetTo ( Tri . A ) , VertexMap . GetTo ( Tri . B ) , VertexMap . GetTo ( Tri . C ) ) ;
int NewTriID = Mesh - > AppendTriangle ( NewTri . A , NewTri . B , NewTri . C , GroupID ) ;
TriangleMap . Add ( TriID , NewTriID ) ;
if ( SetNormals )
{
2022-02-02 07:59:31 -05:00
FVector3f TriNormal ( Mesh - > GetTriNormal ( NewTriID ) ) ; //LWC_TODO: Precision loss
2021-05-11 16:32:22 -04:00
FIndex3i NormalTri ;
for ( int32 j = 0 ; j < 3 ; + + j )
{
NormalTri [ j ] = SetNormals - > AppendElement ( TriNormal ) ;
SetNormals - > SetParentVertex ( NormalTri [ j ] , NewTri [ j ] ) ;
}
SetNormals - > SetTriangle ( NewTriID , NormalTri ) ;
}
}
}
2019-09-10 11:35:20 -04:00
void FDynamicMeshEditor : : AppendNormals ( const FDynamicMesh3 * AppendMesh ,
const FDynamicMeshNormalOverlay * FromNormals , FDynamicMeshNormalOverlay * ToNormals ,
const FIndexMapi & VertexMap , const FIndexMapi & TriangleMap ,
TFunction < FVector3d ( int , const FVector3d & ) > NormalTransform ,
FIndexMapi & NormalMapOut )
{
// copy over normals
for ( int ElemID : FromNormals - > ElementIndicesItr ( ) )
{
int ParentVertID = FromNormals - > GetParentVertex ( ElemID ) ;
FVector3f Normal = FromNormals - > GetElement ( ElemID ) ;
if ( NormalTransform ! = nullptr )
{
Normal = ( FVector3f ) NormalTransform ( ParentVertID , ( FVector3d ) Normal ) ;
}
2020-04-18 18:42:59 -04:00
int NewElemID = ToNormals - > AppendElement ( Normal ) ;
2019-09-10 11:35:20 -04:00
NormalMapOut . Add ( ElemID , NewElemID ) ;
}
// now set new triangles
2021-10-07 02:47:15 -04:00
for ( const TPair < int32 , int32 > & MapTID : TriangleMap . GetForwardMap ( ) )
2019-09-10 11:35:20 -04:00
{
2021-10-07 02:47:15 -04:00
if ( FromNormals - > IsSetTriangle ( MapTID . Key ) )
2019-09-10 11:35:20 -04:00
{
2021-10-07 02:47:15 -04:00
FIndex3i ElemTri = FromNormals - > GetTriangle ( MapTID . Key ) ;
2020-01-27 20:11:15 -05:00
for ( int j = 0 ; j < 3 ; + + j )
{
ElemTri [ j ] = FromNormals - > IsElement ( ElemTri [ j ] ) ? NormalMapOut . GetTo ( ElemTri [ j ] ) : FDynamicMesh3 : : InvalidID ;
}
2021-10-07 02:47:15 -04:00
ToNormals - > SetTriangle ( MapTID . Value , ElemTri ) ;
2019-09-10 11:35:20 -04:00
}
}
}
void FDynamicMeshEditor : : AppendUVs ( const FDynamicMesh3 * AppendMesh ,
const FDynamicMeshUVOverlay * FromUVs , FDynamicMeshUVOverlay * ToUVs ,
const FIndexMapi & VertexMap , const FIndexMapi & TriangleMap ,
FIndexMapi & UVMapOut )
{
// copy over uv elements
for ( int ElemID : FromUVs - > ElementIndicesItr ( ) )
{
FVector2f UV = FromUVs - > GetElement ( ElemID ) ;
2020-04-18 18:42:59 -04:00
int NewElemID = ToUVs - > AppendElement ( UV ) ;
2019-09-10 11:35:20 -04:00
UVMapOut . Add ( ElemID , NewElemID ) ;
}
// now set new triangles
2021-10-07 02:47:15 -04:00
for ( const TPair < int32 , int32 > & MapTID : TriangleMap . GetForwardMap ( ) )
2019-09-10 11:35:20 -04:00
{
2021-10-07 02:47:15 -04:00
if ( FromUVs - > IsSetTriangle ( MapTID . Key ) )
2019-09-10 11:35:20 -04:00
{
2021-10-07 02:47:15 -04:00
FIndex3i ElemTri = FromUVs - > GetTriangle ( MapTID . Key ) ;
2020-01-27 20:11:15 -05:00
for ( int j = 0 ; j < 3 ; + + j )
{
ElemTri [ j ] = FromUVs - > IsElement ( ElemTri [ j ] ) ? UVMapOut . GetTo ( ElemTri [ j ] ) : FDynamicMesh3 : : InvalidID ;
}
2021-10-07 02:47:15 -04:00
ToUVs - > SetTriangle ( MapTID . Value , ElemTri ) ;
2019-09-10 11:35:20 -04:00
}
}
}
2019-10-01 20:41:42 -04:00
2021-04-19 10:47:52 -04:00
void FDynamicMeshEditor : : AppendColors ( const FDynamicMesh3 * AppendMesh ,
const FDynamicMeshColorOverlay * FromOverlay , FDynamicMeshColorOverlay * ToOverlay ,
const FIndexMapi & VertexMap , const FIndexMapi & TriangleMap ,
FIndexMapi & MapOut )
{
// copy over color elements
for ( int ElemID : FromOverlay - > ElementIndicesItr ( ) )
{
int NewElemID = ToOverlay - > AppendElement ( FromOverlay - > GetElement ( ElemID ) ) ;
MapOut . Add ( ElemID , NewElemID ) ;
}
// now set new triangles
2021-10-07 02:47:15 -04:00
for ( const TPair < int32 , int32 > & MapTID : TriangleMap . GetForwardMap ( ) )
2021-04-19 10:47:52 -04:00
{
2021-10-07 02:47:15 -04:00
if ( FromOverlay - > IsSetTriangle ( MapTID . Key ) )
2021-04-19 10:47:52 -04:00
{
2021-10-07 02:47:15 -04:00
FIndex3i ElemTri = FromOverlay - > GetTriangle ( MapTID . Key ) ;
2021-04-19 10:47:52 -04:00
for ( int j = 0 ; j < 3 ; + + j )
{
ElemTri [ j ] = FromOverlay - > IsElement ( ElemTri [ j ] ) ? MapOut . GetTo ( ElemTri [ j ] ) : FDynamicMesh3 : : InvalidID ;
}
2021-10-07 02:47:15 -04:00
ToOverlay - > SetTriangle ( MapTID . Value , ElemTri ) ;
2021-04-19 10:47:52 -04:00
}
}
}
2019-10-01 20:41:42 -04:00
// can these be replaced w/ template function?
2020-11-30 16:26:23 -04:00
namespace UE
{
namespace DynamicMeshEditorInternals
{
2019-10-01 20:41:42 -04:00
// Utility function for ::AppendTriangles()
static int AppendTriangleUVAttribute ( const FDynamicMesh3 * FromMesh , int FromElementID , FDynamicMesh3 * ToMesh , int UVLayerIndex , FMeshIndexMappings & IndexMaps )
{
int NewElementID = IndexMaps . GetNewUV ( UVLayerIndex , FromElementID ) ;
if ( NewElementID = = IndexMaps . InvalidID ( ) )
{
const FDynamicMeshUVOverlay * FromUVOverlay = FromMesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
FDynamicMeshUVOverlay * ToUVOverlay = ToMesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
2020-04-18 18:42:59 -04:00
NewElementID = ToUVOverlay - > AppendElement ( FromUVOverlay - > GetElement ( FromElementID ) ) ;
2019-10-01 20:41:42 -04:00
IndexMaps . SetUV ( UVLayerIndex , FromElementID , NewElementID ) ;
}
return NewElementID ;
}
// Utility function for ::AppendTriangles()
static int AppendTriangleNormalAttribute ( const FDynamicMesh3 * FromMesh , int FromElementID , FDynamicMesh3 * ToMesh , int NormalLayerIndex , FMeshIndexMappings & IndexMaps )
{
int NewElementID = IndexMaps . GetNewNormal ( NormalLayerIndex , FromElementID ) ;
if ( NewElementID = = IndexMaps . InvalidID ( ) )
{
const FDynamicMeshNormalOverlay * FromNormalOverlay = FromMesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
FDynamicMeshNormalOverlay * ToNormalOverlay = ToMesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
2020-04-18 18:42:59 -04:00
NewElementID = ToNormalOverlay - > AppendElement ( FromNormalOverlay - > GetElement ( FromElementID ) ) ;
2019-10-01 20:41:42 -04:00
IndexMaps . SetNormal ( NormalLayerIndex , FromElementID , NewElementID ) ;
}
return NewElementID ;
}
2021-04-19 10:47:52 -04:00
// Utility function for ::AppendTriangles()
static int AppendTriangleColorAttribute ( const FDynamicMesh3 * FromMesh , int FromElementID , FDynamicMesh3 * ToMesh , FMeshIndexMappings & IndexMaps )
{
int NewElementID = IndexMaps . GetNewColor ( FromElementID ) ;
if ( NewElementID = = IndexMaps . InvalidID ( ) )
{
const FDynamicMeshColorOverlay * FromOverlay = FromMesh - > Attributes ( ) - > PrimaryColors ( ) ;
FDynamicMeshColorOverlay * ToOverlay = ToMesh - > Attributes ( ) - > PrimaryColors ( ) ;
NewElementID = ToOverlay - > AppendElement ( FromOverlay - > GetElement ( FromElementID ) ) ;
IndexMaps . SetColor ( FromElementID , NewElementID ) ;
}
return NewElementID ;
}
2019-10-01 20:41:42 -04:00
// Utility function for ::AppendTriangles()
2020-11-30 16:26:23 -04:00
static void AppendTriangleAttributes ( const FDynamicMesh3 * FromMesh , int FromTriangleID , FDynamicMesh3 * ToMesh , int ToTriangleID , FMeshIndexMappings & IndexMaps , FDynamicMeshEditResult & ResultOut )
2019-10-01 20:41:42 -04:00
{
if ( FromMesh - > HasAttributes ( ) = = false | | ToMesh - > HasAttributes ( ) = = false )
{
return ;
}
for ( int UVLayerIndex = 0 ; UVLayerIndex < FMath : : Min ( FromMesh - > Attributes ( ) - > NumUVLayers ( ) , ToMesh - > Attributes ( ) - > NumUVLayers ( ) ) ; UVLayerIndex + + )
{
const FDynamicMeshUVOverlay * FromUVOverlay = FromMesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
FDynamicMeshUVOverlay * ToUVOverlay = ToMesh - > Attributes ( ) - > GetUVLayer ( UVLayerIndex ) ;
2020-01-27 20:11:15 -05:00
if ( FromUVOverlay - > IsSetTriangle ( FromTriangleID ) )
2019-10-01 20:41:42 -04:00
{
2020-01-27 20:11:15 -05:00
FIndex3i FromElemTri = FromUVOverlay - > GetTriangle ( FromTriangleID ) ;
FIndex3i ToElemTri = ToUVOverlay - > GetTriangle ( ToTriangleID ) ;
for ( int j = 0 ; j < 3 ; + + j )
2019-10-01 20:41:42 -04:00
{
2020-01-27 20:11:15 -05:00
check ( FromElemTri [ j ] ! = FDynamicMesh3 : : InvalidID ) ;
2019-12-19 18:07:47 -05:00
int NewElemID = AppendTriangleUVAttribute ( FromMesh , FromElemTri [ j ] , ToMesh , UVLayerIndex , IndexMaps ) ;
2019-10-01 20:41:42 -04:00
ToElemTri [ j ] = NewElemID ;
}
2020-01-27 20:11:15 -05:00
ToUVOverlay - > SetTriangle ( ToTriangleID , ToElemTri ) ;
2019-10-01 20:41:42 -04:00
}
}
2021-01-11 12:43:33 -04:00
for ( int NormalLayerIndex = 0 ; NormalLayerIndex < FMath : : Min ( FromMesh - > Attributes ( ) - > NumNormalLayers ( ) , ToMesh - > Attributes ( ) - > NumNormalLayers ( ) ) ; NormalLayerIndex + + )
2019-10-01 20:41:42 -04:00
{
2021-01-11 12:43:33 -04:00
const FDynamicMeshNormalOverlay * FromNormalOverlay = FromMesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
FDynamicMeshNormalOverlay * ToNormalOverlay = ToMesh - > Attributes ( ) - > GetNormalLayer ( NormalLayerIndex ) ;
2020-01-27 20:11:15 -05:00
if ( FromNormalOverlay - > IsSetTriangle ( FromTriangleID ) )
2019-10-01 20:41:42 -04:00
{
2020-01-27 20:11:15 -05:00
FIndex3i FromElemTri = FromNormalOverlay - > GetTriangle ( FromTriangleID ) ;
FIndex3i ToElemTri = ToNormalOverlay - > GetTriangle ( ToTriangleID ) ;
for ( int j = 0 ; j < 3 ; + + j )
2019-10-01 20:41:42 -04:00
{
2020-01-27 20:11:15 -05:00
check ( FromElemTri [ j ] ! = FDynamicMesh3 : : InvalidID ) ;
2021-06-22 19:35:30 -04:00
int NewElemID = AppendTriangleNormalAttribute ( FromMesh , FromElemTri [ j ] , ToMesh , NormalLayerIndex , IndexMaps ) ;
2019-10-01 20:41:42 -04:00
ToElemTri [ j ] = NewElemID ;
}
2020-01-27 20:11:15 -05:00
ToNormalOverlay - > SetTriangle ( ToTriangleID , ToElemTri ) ;
2019-10-01 20:41:42 -04:00
}
}
2019-12-19 18:07:47 -05:00
2021-04-19 10:47:52 -04:00
if ( FromMesh - > Attributes ( ) - > HasPrimaryColors ( ) & & ToMesh - > Attributes ( ) - > HasPrimaryColors ( ) )
{
const FDynamicMeshColorOverlay * FromOverlay = FromMesh - > Attributes ( ) - > PrimaryColors ( ) ;
FDynamicMeshColorOverlay * ToOverlay = ToMesh - > Attributes ( ) - > PrimaryColors ( ) ;
if ( FromOverlay - > IsSetTriangle ( FromTriangleID ) )
{
FIndex3i FromElemTri = FromOverlay - > GetTriangle ( FromTriangleID ) ;
FIndex3i ToElemTri = ToOverlay - > GetTriangle ( ToTriangleID ) ;
for ( int j = 0 ; j < 3 ; + + j )
{
check ( FromElemTri [ j ] ! = FDynamicMesh3 : : InvalidID ) ;
int NewElemID = AppendTriangleColorAttribute ( FromMesh , FromElemTri [ j ] , ToMesh , IndexMaps ) ;
ToElemTri [ j ] = NewElemID ;
}
ToOverlay - > SetTriangle ( ToTriangleID , ToElemTri ) ;
}
}
2019-12-19 18:07:47 -05:00
if ( FromMesh - > Attributes ( ) - > HasMaterialID ( ) & & ToMesh - > Attributes ( ) - > HasMaterialID ( ) )
{
const FDynamicMeshMaterialAttribute * FromMaterialIDs = FromMesh - > Attributes ( ) - > GetMaterialID ( ) ;
FDynamicMeshMaterialAttribute * ToMaterialIDs = ToMesh - > Attributes ( ) - > GetMaterialID ( ) ;
ToMaterialIDs - > SetValue ( ToTriangleID , FromMaterialIDs - > GetValue ( FromTriangleID ) ) ;
}
2020-11-01 22:12:11 -04:00
2020-11-26 20:12:25 -04:00
int NumPolygroupLayers = FMath : : Min ( FromMesh - > Attributes ( ) - > NumPolygroupLayers ( ) , ToMesh - > Attributes ( ) - > NumPolygroupLayers ( ) ) ;
for ( int PolygroupLayerIndex = 0 ; PolygroupLayerIndex < NumPolygroupLayers ; PolygroupLayerIndex + + )
{
// TODO: remap groups? this will be somewhat expensive...
const FDynamicMeshPolygroupAttribute * FromPolygroups = FromMesh - > Attributes ( ) - > GetPolygroupLayer ( PolygroupLayerIndex ) ;
FDynamicMeshPolygroupAttribute * ToPolygroups = ToMesh - > Attributes ( ) - > GetPolygroupLayer ( PolygroupLayerIndex ) ;
ToPolygroups - > SetValue ( ToTriangleID , FromPolygroups - > GetValue ( FromTriangleID ) ) ;
}
2020-11-30 16:26:23 -04:00
}
2020-11-26 20:12:25 -04:00
2020-11-30 16:26:23 -04:00
// Utility function for ::AppendTriangles()
2022-03-29 20:04:24 -04:00
static void AppendVertexAttributes ( const FDynamicMesh3 * FromMesh , FDynamicMesh3 * ToMesh , FMeshIndexMappings & IndexMaps )
2020-11-30 16:26:23 -04:00
{
2020-12-01 13:49:59 -04:00
if ( FromMesh - > HasAttributes ( ) = = false | | ToMesh - > HasAttributes ( ) = = false )
{
return ;
}
2022-03-29 20:04:24 -04:00
int NumWeightLayers = FMath : : Min ( FromMesh - > Attributes ( ) - > NumWeightLayers ( ) , ToMesh - > Attributes ( ) - > NumWeightLayers ( ) ) ;
for ( int WeightLayerIndex = 0 ; WeightLayerIndex < NumWeightLayers ; WeightLayerIndex + + )
{
const FDynamicMeshWeightAttribute * FromWeights = FromMesh - > Attributes ( ) - > GetWeightLayer ( WeightLayerIndex ) ;
FDynamicMeshWeightAttribute * ToWeights = ToMesh - > Attributes ( ) - > GetWeightLayer ( WeightLayerIndex ) ;
for ( const TPair < int32 , int32 > & MapVID : IndexMaps . GetVertexMap ( ) . GetForwardMap ( ) )
{
float Weight ;
FromWeights - > GetValue ( MapVID . Key , & Weight ) ;
ToWeights - > SetValue ( MapVID . Value , & Weight ) ;
}
}
2021-05-04 23:49:23 -04:00
// Copy skin weight and generic attributes after full IndexMaps have been created.
for ( const TPair < FName , TUniquePtr < FDynamicMeshVertexSkinWeightsAttribute > > & AttribPair : FromMesh - > Attributes ( ) - > GetSkinWeightsAttributes ( ) )
{
FDynamicMeshVertexSkinWeightsAttribute * ToAttrib = ToMesh - > Attributes ( ) - > GetSkinWeightsAttribute ( AttribPair . Key ) ;
if ( ToAttrib )
{
ToAttrib - > CopyThroughMapping ( AttribPair . Value . Get ( ) , IndexMaps ) ;
}
}
2020-11-01 22:12:11 -04:00
for ( const TPair < FName , TUniquePtr < FDynamicMeshAttributeBase > > & AttribPair : FromMesh - > Attributes ( ) - > GetAttachedAttributes ( ) )
{
if ( ToMesh - > Attributes ( ) - > HasAttachedAttribute ( AttribPair . Key ) )
{
FDynamicMeshAttributeBase * ToAttrib = ToMesh - > Attributes ( ) - > GetAttachedAttribute ( AttribPair . Key ) ;
ToAttrib - > CopyThroughMapping ( AttribPair . Value . Get ( ) , IndexMaps ) ;
}
}
2019-10-01 20:41:42 -04:00
}
2020-11-30 16:26:23 -04:00
} } // namespace UE::DynamicMeshEditorInternals
2019-10-01 20:41:42 -04:00
2019-12-19 18:07:47 -05:00
void FDynamicMeshEditor : : AppendTriangles ( const FDynamicMesh3 * SourceMesh , const TArrayView < const int > & SourceTriangles , FMeshIndexMappings & IndexMaps , FDynamicMeshEditResult & ResultOut , bool bComputeTriangleMap )
2019-10-01 20:41:42 -04:00
{
2020-11-30 16:26:23 -04:00
using namespace UE : : DynamicMeshEditorInternals ;
2019-10-01 20:41:42 -04:00
ResultOut . Reset ( ) ;
IndexMaps . Initialize ( Mesh ) ;
2022-01-12 14:28:31 -05:00
int DefaultGroupID = FDynamicMesh3 : : InvalidID ;
2019-10-01 20:41:42 -04:00
for ( int SourceTriangleID : SourceTriangles )
{
check ( SourceMesh - > IsTriangle ( SourceTriangleID ) ) ;
if ( SourceMesh - > IsTriangle ( SourceTriangleID ) = = false )
{
continue ; // ignore missing triangles
}
FIndex3i Tri = SourceMesh - > GetTriangle ( SourceTriangleID ) ;
// FindOrCreateDuplicateGroup
2020-06-23 18:40:00 -04:00
int NewGroupID = FDynamicMesh3 : : InvalidID ;
2022-01-12 14:28:31 -05:00
if ( Mesh - > HasTriangleGroups ( ) )
2019-10-01 20:41:42 -04:00
{
2022-01-12 14:28:31 -05:00
if ( SourceMesh - > HasTriangleGroups ( ) )
2020-06-23 18:40:00 -04:00
{
2022-01-12 14:28:31 -05:00
int SourceGroupID = SourceMesh - > GetTriangleGroup ( SourceTriangleID ) ;
if ( SourceGroupID > = 0 )
2020-08-11 01:36:57 -04:00
{
2022-01-12 14:28:31 -05:00
NewGroupID = IndexMaps . GetNewGroup ( SourceGroupID ) ;
if ( NewGroupID = = IndexMaps . InvalidID ( ) )
{
NewGroupID = Mesh - > AllocateTriangleGroup ( ) ;
IndexMaps . SetGroup ( SourceGroupID , NewGroupID ) ;
ResultOut . NewGroups . Add ( NewGroupID ) ;
}
2020-08-11 01:36:57 -04:00
}
2020-06-23 18:40:00 -04:00
}
2022-01-12 14:28:31 -05:00
else
{
// If the source mesh does not have triangle groups, but the destination
// mesh does, create a default group for all triangles.
if ( DefaultGroupID = = FDynamicMesh3 : : InvalidID )
{
DefaultGroupID = Mesh - > AllocateTriangleGroup ( ) ;
ResultOut . NewGroups . Add ( DefaultGroupID ) ;
}
NewGroupID = DefaultGroupID ;
}
2019-10-01 20:41:42 -04:00
}
// FindOrCreateDuplicateVertex
FIndex3i NewTri ;
for ( int j = 0 ; j < 3 ; + + j )
{
int SourceVertexID = Tri [ j ] ;
int NewVertexID = IndexMaps . GetNewVertex ( SourceVertexID ) ;
if ( NewVertexID = = IndexMaps . InvalidID ( ) )
{
NewVertexID = Mesh - > AppendVertex ( * SourceMesh , SourceVertexID ) ;
IndexMaps . SetVertex ( SourceVertexID , NewVertexID ) ;
ResultOut . NewVertices . Add ( NewVertexID ) ;
}
NewTri [ j ] = NewVertexID ;
}
int NewTriangleID = Mesh - > AppendTriangle ( NewTri , NewGroupID ) ;
2019-12-19 18:07:47 -05:00
if ( bComputeTriangleMap )
{
IndexMaps . SetTriangle ( SourceTriangleID , NewTriangleID ) ;
}
2019-10-01 20:41:42 -04:00
ResultOut . NewTriangles . Add ( NewTriangleID ) ;
2020-11-30 16:26:23 -04:00
AppendTriangleAttributes ( SourceMesh , SourceTriangleID , Mesh , NewTriangleID , IndexMaps , ResultOut ) ;
2019-10-01 20:41:42 -04:00
//Mesh->CheckValidity(true);
}
2020-12-01 13:49:59 -04:00
2022-03-29 20:04:24 -04:00
AppendVertexAttributes ( SourceMesh , Mesh , IndexMaps ) ;
2020-12-01 13:49:59 -04:00
2019-10-01 20:41:42 -04:00
}
2019-12-19 18:07:47 -05:00
bool FDynamicMeshEditor : : SplitMesh ( const FDynamicMesh3 * SourceMesh , TArray < FDynamicMesh3 > & SplitMeshes , TFunctionRef < int ( int ) > TriIDToMeshID , int DeleteMeshID )
{
2020-11-30 16:26:23 -04:00
using namespace UE : : DynamicMeshEditorInternals ;
2019-12-19 18:07:47 -05:00
TMap < int , int > MeshIDToIndex ;
int NumMeshes = 0 ;
bool bAlsoDelete = false ;
for ( int TID : SourceMesh - > TriangleIndicesItr ( ) )
{
int MeshID = TriIDToMeshID ( TID ) ;
if ( MeshID = = DeleteMeshID )
{
bAlsoDelete = true ;
continue ;
}
if ( ! MeshIDToIndex . Contains ( MeshID ) )
{
MeshIDToIndex . Add ( MeshID , NumMeshes + + ) ;
}
}
if ( ! bAlsoDelete & & NumMeshes < 2 )
{
return false ; // nothing to do, so don't bother filling the split meshes array
}
SplitMeshes . Reset ( ) ;
SplitMeshes . SetNum ( NumMeshes ) ;
// enable matching attributes
for ( FDynamicMesh3 & M : SplitMeshes )
{
2020-11-24 14:10:15 -04:00
M . EnableMeshComponents ( SourceMesh - > GetComponentsFlags ( ) ) ;
2019-12-19 18:07:47 -05:00
if ( SourceMesh - > HasAttributes ( ) )
{
M . EnableAttributes ( ) ;
M . Attributes ( ) - > EnableMatchingAttributes ( * SourceMesh - > Attributes ( ) ) ;
}
}
if ( NumMeshes = = 0 ) // full delete case, just leave the empty mesh
{
return true ;
}
TArray < FMeshIndexMappings > Mappings ; Mappings . Reserve ( NumMeshes ) ;
FDynamicMeshEditResult UnusedInvalidResultAccumulator ; // only here because some functions require it
for ( int Idx = 0 ; Idx < NumMeshes ; Idx + + )
{
FMeshIndexMappings & Map = Mappings . Emplace_GetRef ( ) ;
Map . Initialize ( & SplitMeshes [ Idx ] ) ;
}
for ( int SourceTID : SourceMesh - > TriangleIndicesItr ( ) )
{
int MeshID = TriIDToMeshID ( SourceTID ) ;
if ( MeshID = = DeleteMeshID )
{
continue ; // just skip triangles w/ the Delete Mesh ID
}
int MeshIndex = MeshIDToIndex [ MeshID ] ;
FDynamicMesh3 & Mesh = SplitMeshes [ MeshIndex ] ;
FMeshIndexMappings & IndexMaps = Mappings [ MeshIndex ] ;
FIndex3i Tri = SourceMesh - > GetTriangle ( SourceTID ) ;
2022-02-25 09:37:48 -05:00
// Find or create corresponding triangle group
int NewGID = FDynamicMesh3 : : InvalidID ;
if ( SourceMesh - > HasTriangleGroups ( ) )
{
int SourceGroupID = SourceMesh - > GetTriangleGroup ( SourceTID ) ;
if ( SourceGroupID > = 0 )
{
NewGID = IndexMaps . GetNewGroup ( SourceGroupID ) ;
if ( NewGID = = IndexMaps . InvalidID ( ) )
{
NewGID = Mesh . AllocateTriangleGroup ( ) ;
IndexMaps . SetGroup ( SourceGroupID , NewGID ) ;
}
}
}
2019-12-19 18:07:47 -05:00
FIndex3i NewTri ;
for ( int j = 0 ; j < 3 ; + + j )
{
int SourceVID = Tri [ j ] ;
int NewVID = IndexMaps . GetNewVertex ( SourceVID ) ;
if ( NewVID = = IndexMaps . InvalidID ( ) )
{
NewVID = Mesh . AppendVertex ( * SourceMesh , SourceVID ) ;
IndexMaps . SetVertex ( SourceVID , NewVID ) ;
}
NewTri [ j ] = NewVID ;
}
int NewTID = Mesh . AppendTriangle ( NewTri , NewGID ) ;
IndexMaps . SetTriangle ( SourceTID , NewTID ) ;
2020-11-30 16:26:23 -04:00
AppendTriangleAttributes ( SourceMesh , SourceTID , & Mesh , NewTID , IndexMaps , UnusedInvalidResultAccumulator ) ;
}
for ( int Idx = 0 ; Idx < NumMeshes ; Idx + + )
{
2022-03-29 20:04:24 -04:00
AppendVertexAttributes ( SourceMesh , & SplitMeshes [ Idx ] , Mappings [ Idx ] ) ;
2019-12-19 18:07:47 -05:00
}
return true ;
}