2023-01-21 13:02:53 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "Commands/RetriangulateGeometrySelectionCommand.h"
# include "ToolContextInterfaces.h"
# include "UDynamicMesh.h"
# include "DynamicMesh/DynamicMesh3.h"
# include "DynamicMesh/DynamicMeshChangeTracker.h"
# include "DynamicMeshEditor.h"
# include "Changes/MeshChange.h"
# include "Selections/GeometrySelectionUtil.h"
# include "Operations/PolygroupRemesh.h"
# include "ConstrainedDelaunay2.h"
# include "Operations/SimpleHoleFiller.h"
# include "MeshRegionBoundaryLoops.h"
# include "DynamicMesh/MeshIndexUtil.h"
ModelingMode: selection system bugfixes & improvements.
Add DynamicMeshSelector::UpdateAfterGeometryEdit API. StaticMeshSelector implementation updates static mesh after an Edit instead of emitting MeshChange on temporary DynamicMesh.
Delete and Retriangulate Commands now use this API instead of directly emitting transaction, so now these commands work properly on Static Meshes.
FStaticMeshComponentSelectorFactory::CanBuildForTarget now only allows UStaticMeshComponent specifically, filtering out subclasses. This is not ideal but I don't know what else we can do for now, there are many subclasses like ISMC, SplineMeshComponent, etc, that will not work w/ the Selection system. Also now filtering out Engine assets and cooked assets.
ModelingToolsEditorMode now listens for blueprint pre-compiles, and when this occurs, clears the active selection and selection targets. This is necessary because if the selection Component was part of a BP, on recompile it is re-instanced and the old pointer goes stale. Possibly can handle this better or at a more granular level, but clearing the selection is safest. This currently results in things not being undoable because the FChange transactions are on the "old" UDynamicMesh that no longer exists.
UModelingToolsEditorMode::UpdateSelectionManagerOnEditorSelectionChange now does a more thorough job of inspecting the current Actor/Component selection to find Components that could work w/ the selection system.
#rb lonnie.li
[CL 26133067 by ryan schmidt in ue5-main branch]
2023-06-20 16:23:43 -04:00
# include "Selection/DynamicMeshSelector.h"
2023-01-21 13:02:53 -05:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(RetriangulateGeometrySelectionCommand)
using namespace UE : : Geometry ;
# define LOCTEXT_NAMESPACE "URetriangulateGeometrySelectionCommand"
FText URetriangulateGeometrySelectionCommand : : GetCommandShortString ( ) const
{
return LOCTEXT ( " ShortString " , " Retriangulate Selection " ) ;
}
bool URetriangulateGeometrySelectionCommand : : CanExecuteCommandForSelection ( UGeometrySelectionEditCommandArguments * SelectionArgs )
{
return SelectionArgs - > IsMatchingType ( FGeometryIdentifier : : ETargetType : : MeshContainer , FGeometryIdentifier : : EObjectType : : DynamicMesh )
& & ( SelectionArgs - > SelectionHandle . Selection - > ElementType = = EGeometryElementType : : Face )
& & ( SelectionArgs - > SelectionHandle . Selection - > TopologyType = = EGeometryTopologyType : : Polygroup ) ;
}
void URetriangulateGeometrySelectionCommand : : ExecuteCommandForSelection ( UGeometrySelectionEditCommandArguments * SelectionArgs , UInteractiveCommandResult * * Result )
{
ModelingMode: selection system bugfixes & improvements.
Add DynamicMeshSelector::UpdateAfterGeometryEdit API. StaticMeshSelector implementation updates static mesh after an Edit instead of emitting MeshChange on temporary DynamicMesh.
Delete and Retriangulate Commands now use this API instead of directly emitting transaction, so now these commands work properly on Static Meshes.
FStaticMeshComponentSelectorFactory::CanBuildForTarget now only allows UStaticMeshComponent specifically, filtering out subclasses. This is not ideal but I don't know what else we can do for now, there are many subclasses like ISMC, SplineMeshComponent, etc, that will not work w/ the Selection system. Also now filtering out Engine assets and cooked assets.
ModelingToolsEditorMode now listens for blueprint pre-compiles, and when this occurs, clears the active selection and selection targets. This is necessary because if the selection Component was part of a BP, on recompile it is re-instanced and the old pointer goes stale. Possibly can handle this better or at a more granular level, but clearing the selection is safest. This currently results in things not being undoable because the FChange transactions are on the "old" UDynamicMesh that no longer exists.
UModelingToolsEditorMode::UpdateSelectionManagerOnEditorSelectionChange now does a more thorough job of inspecting the current Actor/Component selection to find Components that could work w/ the selection system.
#rb lonnie.li
[CL 26133067 by ryan schmidt in ue5-main branch]
2023-06-20 16:23:43 -04:00
IGeometrySelector * BaseSelector = SelectionArgs - > SelectionHandle . Selector ;
if ( ! ensure ( BaseSelector ! = nullptr ) )
{
UE_LOG ( LogGeometry , Warning , TEXT ( " URetriangulateGeometrySelectionCommand: Retriangulate Selection requires Selector be provided in Selection Arguments " ) ) ;
return ;
}
2023-01-21 13:02:53 -05:00
if ( Result ! = nullptr )
{
* Result = nullptr ;
}
// should have been verified by CanExecute
check ( SelectionArgs - > IsMatchingType ( FGeometryIdentifier : : ETargetType : : MeshContainer , FGeometryIdentifier : : EObjectType : : DynamicMesh ) ) ;
ModelingMode: selection system bugfixes & improvements.
Add DynamicMeshSelector::UpdateAfterGeometryEdit API. StaticMeshSelector implementation updates static mesh after an Edit instead of emitting MeshChange on temporary DynamicMesh.
Delete and Retriangulate Commands now use this API instead of directly emitting transaction, so now these commands work properly on Static Meshes.
FStaticMeshComponentSelectorFactory::CanBuildForTarget now only allows UStaticMeshComponent specifically, filtering out subclasses. This is not ideal but I don't know what else we can do for now, there are many subclasses like ISMC, SplineMeshComponent, etc, that will not work w/ the Selection system. Also now filtering out Engine assets and cooked assets.
ModelingToolsEditorMode now listens for blueprint pre-compiles, and when this occurs, clears the active selection and selection targets. This is necessary because if the selection Component was part of a BP, on recompile it is re-instanced and the old pointer goes stale. Possibly can handle this better or at a more granular level, but clearing the selection is safest. This currently results in things not being undoable because the FChange transactions are on the "old" UDynamicMesh that no longer exists.
UModelingToolsEditorMode::UpdateSelectionManagerOnEditorSelectionChange now does a more thorough job of inspecting the current Actor/Component selection to find Components that could work w/ the selection system.
#rb lonnie.li
[CL 26133067 by ryan schmidt in ue5-main branch]
2023-06-20 16:23:43 -04:00
// TODO: extremely hardcoded behavior right here. Need a way to make this more generic, however
// having the UpdateAfterGeometryEdit function in the base GeometrySelector does not make sense as
// it is specific to meshes. Probably this Command needs to be specialized for Mesh Edits.
FBaseDynamicMeshSelector * BaseDynamicMeshSelector = static_cast < FBaseDynamicMeshSelector * > ( BaseSelector ) ;
2023-01-21 13:02:53 -05:00
// collect up all our inputs
UDynamicMesh * MeshObject = SelectionArgs - > SelectionHandle . Identifier . GetAsObjectType < UDynamicMesh > ( ) ;
check ( MeshObject ! = nullptr ) ;
const FGeometrySelection * Selection = SelectionArgs - > SelectionHandle . Selection ;
bool bTrackChanges = SelectionArgs - > HasTransactionsAPI ( ) ;
TUniquePtr < FDynamicMeshChange > DynamicMeshChange ; // only initialized if bTrackChanges == true
// apply the Retriangulate operation
MeshObject - > EditMesh ( [ & ] ( FDynamicMesh3 & EditMesh )
{
// build list of triangles from whatever the selection contains
TSet < int32 > TriangleList ;
//UE::Geometry::FPolygroupSet UsePolygroupSet = ...; // need to support this eventually
UE : : Geometry : : EnumerateSelectionTriangles ( * Selection , EditMesh ,
[ & ] ( int32 TriangleID ) { TriangleList . Add ( TriangleID ) ; } ) ;
if ( TriangleList . Num ( ) = = 0 )
{
for ( int32 tid : EditMesh . TriangleIndicesItr ( ) )
{
TriangleList . Add ( tid ) ;
}
}
// mark triangles for change
FDynamicMeshChangeTracker ChangeTracker ( & EditMesh ) ;
FGroupTopology Topology ( & EditMesh , true ) ;
if ( bTrackChanges )
{
ChangeTracker . BeginChange ( ) ;
}
if ( TriangleList . Num ( ) = = EditMesh . TriangleCount ( ) )
{
if ( bTrackChanges )
{
ChangeTracker . SaveTriangles ( TriangleList , true ) ;
}
FPolygroupRemesh Remesh ( & EditMesh , & Topology , ConstrainedDelaunayTriangulate < double > ) ;
bool bSuccess = Remesh . Compute ( ) ;
}
else
{
// ported from UEditMeshPolygonsTool::ApplyRetriangulate(), should be moved to a standalone geometry op
const FDynamicMeshAttributeSet * Attributes = EditMesh . HasAttributes ( ) ? EditMesh . Attributes ( ) : nullptr ;
TSet < int32 > SelectedGroupIDs ;
for ( int32 tid : TriangleList )
{
SelectedGroupIDs . Add ( Topology . GetGroupID ( tid ) ) ;
}
int32 nCompleted = 0 ;
FDynamicMeshEditor Editor ( & EditMesh ) ;
for ( int32 GroupID : SelectedGroupIDs )
{
const TArray < int32 > & Triangles = Topology . GetGroupTriangles ( GroupID ) ;
ChangeTracker . SaveTriangles ( Triangles , true ) ;
FMeshRegionBoundaryLoops RegionLoops ( & EditMesh , Triangles , true ) ;
if ( ! RegionLoops . bFailed & & RegionLoops . Loops . Num ( ) = = 1 & & Triangles . Num ( ) > 1 )
{
TArray < FMeshRegionBoundaryLoops : : VidOverlayMap < FVector2f > > VidUVMaps ;
if ( Attributes )
{
for ( int i = 0 ; i < Attributes - > NumUVLayers ( ) ; + + i )
{
VidUVMaps . Emplace ( ) ;
RegionLoops . GetLoopOverlayMap ( RegionLoops . Loops [ 0 ] , * Attributes - > GetUVLayer ( i ) , VidUVMaps . Last ( ) ) ;
}
}
// We don't want to remove isolated vertices while removing triangles because we don't
// want to throw away boundary verts. However, this means that we'll have to go back
// through these vertices later to throw away isolated internal verts.
TArray < int32 > OldVertices ;
UE : : Geometry : : TriangleToVertexIDs ( & EditMesh , Triangles , OldVertices ) ;
Editor . RemoveTriangles ( Topology . GetGroupTriangles ( GroupID ) , false ) ;
RegionLoops . Loops [ 0 ] . Reverse ( ) ;
FSimpleHoleFiller Filler ( & EditMesh , RegionLoops . Loops [ 0 ] ) ;
Filler . FillType = FSimpleHoleFiller : : EFillType : : PolygonEarClipping ;
Filler . Fill ( GroupID ) ;
// Throw away any of the old verts that are still isolated (they were in the interior of the group)
for ( int32 Vid : OldVertices )
{
if ( ! EditMesh . IsReferencedVertex ( Vid ) )
{
constexpr bool bPreserveManifold = false ;
EditMesh . RemoveVertex ( Vid , bPreserveManifold ) ;
}
}
if ( Attributes )
{
for ( int i = 0 ; i < Attributes - > NumUVLayers ( ) ; + + i )
{
RegionLoops . UpdateLoopOverlayMapValidity ( VidUVMaps [ i ] , * Attributes - > GetUVLayer ( i ) ) ;
}
Filler . UpdateAttributes ( VidUVMaps ) ;
}
nCompleted + + ;
}
}
}
// extract the change record
if ( bTrackChanges )
{
DynamicMeshChange = ChangeTracker . EndChange ( ) ;
}
} , EDynamicMeshChangeType : : GeneralEdit , EDynamicMeshAttributeChangeFlags : : Unknown , false ) ;
// emit change
if ( bTrackChanges & & DynamicMeshChange . IsValid ( ) )
{
SelectionArgs - > GetTransactionsAPI ( ) - > BeginUndoTransaction ( GetCommandShortString ( ) ) ;
ModelingMode: selection system bugfixes & improvements.
Add DynamicMeshSelector::UpdateAfterGeometryEdit API. StaticMeshSelector implementation updates static mesh after an Edit instead of emitting MeshChange on temporary DynamicMesh.
Delete and Retriangulate Commands now use this API instead of directly emitting transaction, so now these commands work properly on Static Meshes.
FStaticMeshComponentSelectorFactory::CanBuildForTarget now only allows UStaticMeshComponent specifically, filtering out subclasses. This is not ideal but I don't know what else we can do for now, there are many subclasses like ISMC, SplineMeshComponent, etc, that will not work w/ the Selection system. Also now filtering out Engine assets and cooked assets.
ModelingToolsEditorMode now listens for blueprint pre-compiles, and when this occurs, clears the active selection and selection targets. This is necessary because if the selection Component was part of a BP, on recompile it is re-instanced and the old pointer goes stale. Possibly can handle this better or at a more granular level, but clearing the selection is safest. This currently results in things not being undoable because the FChange transactions are on the "old" UDynamicMesh that no longer exists.
UModelingToolsEditorMode::UpdateSelectionManagerOnEditorSelectionChange now does a more thorough job of inspecting the current Actor/Component selection to find Components that could work w/ the selection system.
#rb lonnie.li
[CL 26133067 by ryan schmidt in ue5-main branch]
2023-06-20 16:23:43 -04:00
BaseDynamicMeshSelector - > UpdateAfterGeometryEdit (
SelectionArgs - > GetTransactionsAPI ( ) , true , MoveTemp ( DynamicMeshChange ) , GetCommandShortString ( ) ) ;
2023-01-21 13:02:53 -05:00
SelectionArgs - > GetTransactionsAPI ( ) - > EndUndoTransaction ( ) ;
}
if ( Result ! = nullptr )
{
UGeometrySelectionEditCommandResult * NewResult = NewObject < UGeometrySelectionEditCommandResult > ( ) ;
NewResult - > SourceHandle = SelectionArgs - > SelectionHandle ;
// todo...
//NewResult->OutputSelection = *NewResult->SourceHandle.Selection;
* Result = NewResult ;
}
}
# undef LOCTEXT_NAMESPACE