Mesh Editor: Minor refactoring to improve extensibility

- Mesh editor commands no longer use class default objects
- Tweaked some internal functions to make them more general purpose
#rb none

[CL 3428862 by Mike Fricker in Dev-Geometry branch]
This commit is contained in:
Mike Fricker
2017-05-08 15:03:23 -04:00
parent 57ad279363
commit 777dffe8ff
7 changed files with 131 additions and 99 deletions

View File

@@ -10,6 +10,24 @@
#define LOCTEXT_NAMESPACE "MeshEditorCommands"
namespace MeshEditorCommands
{
const TArray<UMeshEditorCommand*>& Get()
{
static UMeshEditorCommandList* MeshEditorCommandList = nullptr;
if( MeshEditorCommandList == nullptr )
{
MeshEditorCommandList = NewObject<UMeshEditorCommandList>();
MeshEditorCommandList->AddToRoot();
MeshEditorCommandList->HarvestMeshEditorCommands();
}
return MeshEditorCommandList->MeshEditorCommands;
}
}
FUIAction UMeshEditorInstantCommand::MakeUIAction( IMeshEditorModeUIContract& MeshEditorMode )
{
const EEditableMeshElementType ElementType = GetElementType();
@@ -86,15 +104,11 @@ void FMeshEditorCommonCommands::RegisterCommands()
UI_COMMAND(SetPolygonSelectionMode, "Set Polygon Selection Mode", "Sets the selection mode so that only polygons will be selected.", EUserInterfaceActionType::None, FInputChord(EKeys::Three));
UI_COMMAND(SetAnySelectionMode, "Set Any Selection Mode", "Sets the selection mode so that any element type may be selected.", EUserInterfaceActionType::None, FInputChord(EKeys::Four));
for( TObjectIterator<UMeshEditorCommand> CommandCDOIter( RF_NoFlags ); CommandCDOIter; ++CommandCDOIter )
for( UMeshEditorCommand* Command : MeshEditorCommands::Get() )
{
UMeshEditorCommand* CommandCDO = *CommandCDOIter;
if( !( CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract ) )
if( Command->GetElementType() == EEditableMeshElementType::Invalid )
{
if( CommandCDO->GetElementType() == EEditableMeshElementType::Invalid )
{
CommandCDO->RegisterUICommand( this );
}
Command->RegisterUICommand( this );
}
}
}
@@ -130,15 +144,11 @@ void FMeshEditorVertexCommands::RegisterCommands()
UI_COMMAND(WeldVertices, "Weld Vertices", "Weld the selected vertices, keeping the first selected vertex.", EUserInterfaceActionType::Button, FInputChord());
for( TObjectIterator<UMeshEditorCommand> CommandCDOIter( RF_NoFlags ); CommandCDOIter; ++CommandCDOIter )
for( UMeshEditorCommand* Command : MeshEditorCommands::Get() )
{
UMeshEditorCommand* CommandCDO = *CommandCDOIter;
if( !( CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract ) )
if( Command->GetElementType() == EEditableMeshElementType::Vertex )
{
if( CommandCDO->GetElementType() == EEditableMeshElementType::Vertex )
{
CommandCDO->RegisterUICommand( this );
}
Command->RegisterUICommand( this );
}
}
}
@@ -159,15 +169,11 @@ void FMeshEditorEdgeCommands::RegisterCommands()
UI_COMMAND(SelectEdgeLoop, "Select Edge Loop", "Select the edge loops which contain the selected edges.", EUserInterfaceActionType::Button, FInputChord(EKeys::Two, EModifierKey::Shift));
for( TObjectIterator<UMeshEditorCommand> CommandCDOIter( RF_NoFlags ); CommandCDOIter; ++CommandCDOIter )
for( UMeshEditorCommand* Command : MeshEditorCommands::Get() )
{
UMeshEditorCommand* CommandCDO = *CommandCDOIter;
if( !( CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract ) )
if( Command->GetElementType() == EEditableMeshElementType::Edge )
{
if( CommandCDO->GetElementType() == EEditableMeshElementType::Edge )
{
CommandCDO->RegisterUICommand( this );
}
Command->RegisterUICommand( this );
}
}
}
@@ -189,17 +195,28 @@ void FMeshEditorPolygonCommands::RegisterCommands()
UI_COMMAND(TriangulatePolygon, "Triangulate Polygon", "Triangulate the currently selected polygons.", EUserInterfaceActionType::Button, FInputChord(EKeys::T));
UI_COMMAND(AssignMaterial, "Assign Material", "Assigns the highlighted material in the Content Browser to the currently selected polygons.", EUserInterfaceActionType::Button, FInputChord(EKeys::M));
for( UMeshEditorCommand* Command : MeshEditorCommands::Get() )
{
if( Command->GetElementType() == EEditableMeshElementType::Polygon )
{
Command->RegisterUICommand( this );
}
}
}
void UMeshEditorCommandList::HarvestMeshEditorCommands()
{
MeshEditorCommands.Reset();
for( TObjectIterator<UMeshEditorCommand> CommandCDOIter( RF_NoFlags ); CommandCDOIter; ++CommandCDOIter )
{
UMeshEditorCommand* CommandCDO = *CommandCDOIter;
if( !( CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract ) )
{
if( CommandCDO->GetElementType() == EEditableMeshElementType::Polygon )
{
CommandCDO->RegisterUICommand( this );
}
MeshEditorCommands.Add( NewObject<UMeshEditorCommand>( this, CommandCDO->GetClass() ) );
}
}
}
#undef LOCTEXT_NAMESPACE

View File

@@ -575,39 +575,35 @@ void FMeshEditorMode::BindCommands()
RegisterPolygonCommand( MeshEditorPolygonCommands.TriangulatePolygon, FExecuteAction::CreateLambda( [this] { TriangulateSelectedPolygons(); } ) );
RegisterPolygonCommand( MeshEditorPolygonCommands.AssignMaterial, FExecuteAction::CreateLambda( [this] { AssignSelectedMaterialToSelectedPolygons(); } ) );
for( TObjectIterator<UMeshEditorCommand> CommandCDOIter( RF_NoFlags ); CommandCDOIter; ++CommandCDOIter )
for( UMeshEditorCommand* Command : MeshEditorCommands::Get() )
{
UMeshEditorCommand* CommandCDO = *CommandCDOIter;
if( !( CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract ) )
switch( Command->GetElementType() )
{
switch( CommandCDO->GetElementType() )
{
case EEditableMeshElementType::Invalid:
{
// Common action
FUIAction UIAction = CommandCDO->MakeUIAction( *this );
CommonActions.Emplace( CommandCDO->GetUICommandInfo(), UIAction );
VertexActions.Emplace( CommandCDO->GetUICommandInfo(), UIAction );
EdgeActions.Emplace( CommandCDO->GetUICommandInfo(), UIAction );
PolygonActions.Emplace( CommandCDO->GetUICommandInfo(), UIAction );
}
break;
case EEditableMeshElementType::Invalid:
{
// Common action
FUIAction UIAction = Command->MakeUIAction( *this );
CommonActions.Emplace( Command->GetUICommandInfo(), UIAction );
VertexActions.Emplace( Command->GetUICommandInfo(), UIAction );
EdgeActions.Emplace( Command->GetUICommandInfo(), UIAction );
PolygonActions.Emplace( Command->GetUICommandInfo(), UIAction );
}
break;
case EEditableMeshElementType::Vertex:
VertexActions.Emplace( CommandCDO->GetUICommandInfo(), CommandCDO->MakeUIAction( *this ) );
break;
case EEditableMeshElementType::Vertex:
VertexActions.Emplace( Command->GetUICommandInfo(), Command->MakeUIAction( *this ) );
break;
case EEditableMeshElementType::Edge:
EdgeActions.Emplace( CommandCDO->GetUICommandInfo(), CommandCDO->MakeUIAction( *this ) );
break;
case EEditableMeshElementType::Edge:
EdgeActions.Emplace( Command->GetUICommandInfo(), Command->MakeUIAction( *this ) );
break;
case EEditableMeshElementType::Polygon:
PolygonActions.Emplace( CommandCDO->GetUICommandInfo(), CommandCDO->MakeUIAction( *this ) );
break;
case EEditableMeshElementType::Polygon:
PolygonActions.Emplace( Command->GetUICommandInfo(), Command->MakeUIAction( *this ) );
break;
default:
check( 0 );
}
default:
check( 0 );
}
}
@@ -3249,16 +3245,16 @@ void FMeshEditorMode::UpdateActiveAction( const bool bIsActionFinishing )
{
// Check for registered commands that are active right now
bool bFoundValidCommand = false;
for( TObjectIterator<UMeshEditorEditCommand> CommandCDOIter( RF_NoFlags ); CommandCDOIter; ++CommandCDOIter )
for( UMeshEditorCommand* Command : MeshEditorCommands::Get() )
{
UMeshEditorEditCommand* CommandCDO = *CommandCDOIter;
if( !( CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract ) )
UMeshEditorEditCommand* EditCommand = Cast<UMeshEditorEditCommand>( Command );
if( EditCommand != nullptr )
{
if( ActiveAction == CommandCDO->GetCommandName() )
if( ActiveAction == EditCommand->GetCommandName() )
{
CommandCDO->ApplyDuringDrag( *this, ActiveActionInteractor );
EditCommand->ApplyDuringDrag( *this, ActiveActionInteractor );
bIsMovingSelectedMeshElements = CommandCDO->NeedsDraggingInitiated();
bIsMovingSelectedMeshElements = EditCommand->NeedsDraggingInitiated();
// Should always only be one candidate
bFoundValidCommand = true;
@@ -3457,11 +3453,12 @@ void FMeshEditorMode::GetSelectedMeshesAndElements( EEditableMeshElementType Ele
}
void FMeshEditorMode::FindEdgeSplitUnderInteractor( UViewportInteractor* ViewportInteractor, const UEditableMesh* EditableMesh, const TArray<FMeshElement>& EdgeElements, TArray<float>& OutSplits )
bool FMeshEditorMode::FindEdgeSplitUnderInteractor( UViewportInteractor* ViewportInteractor, const UEditableMesh* EditableMesh, const TArray<FMeshElement>& EdgeElements, FEdgeID& OutClosestEdgeID, float& OutSplit )
{
check( ViewportInteractor != nullptr );
OutSplits.Reset();
OutClosestEdgeID = FEdgeID::Invalid;
bool bFoundSplit = false;
// Figure out where to split based on where the interactor is aiming. We'll look at all of the
// selected edges, and choose a split offset based on the closest point along one of those edges
@@ -3513,11 +3510,14 @@ void FMeshEditorMode::FindEdgeSplitUnderInteractor( UViewportInteractor* Viewpor
1.0f );
}
OutSplits.Reset();
OutSplits.Add( ProgressAlongEdge );
bFoundSplit = true;
OutClosestEdgeID = EdgeID;
OutSplit = ProgressAlongEdge;
}
}
}
return bFoundSplit;
}
@@ -4016,10 +4016,10 @@ void FMeshEditorMode::OnViewportInteractionInputAction( FEditorViewportClient& V
}
else
{
for( TObjectIterator<UMeshEditorEditCommand> CommandCDOIter( RF_NoFlags ); CommandCDOIter; ++CommandCDOIter )
for( UMeshEditorCommand* Command : MeshEditorCommands::Get() )
{
UMeshEditorEditCommand* CommandCDO = *CommandCDOIter;
if( !( CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract ) )
UMeshEditorEditCommand* EditCommand = Cast<UMeshEditorEditCommand>( Command );
if( EditCommand != nullptr )
{
FName EquippedAction = NAME_None;
switch( SelectedMeshElementType )
@@ -4037,15 +4037,15 @@ void FMeshEditorMode::OnViewportInteractionInputAction( FEditorViewportClient& V
break;
}
const EEditableMeshElementType CommandElementType = CommandCDO->GetElementType();
if( ( CommandElementType == SelectedMeshElementType || CommandElementType == EEditableMeshElementType::Invalid || CommandElementType == EEditableMeshElementType::Any ) &&
EquippedAction == CommandCDO->GetCommandName() )
const EEditableMeshElementType CommandElementType = EditCommand->GetElementType();
if( ( CommandElementType == SelectedMeshElementType || CommandElementType == EEditableMeshElementType::Invalid || CommandElementType == EEditableMeshElementType::Any ) &&
EquippedAction == EditCommand->GetCommandName() )
{
if( CommandCDO->TryStartingToDrag( *this, ViewportInteractor ) )
if( EditCommand->TryStartingToDrag( *this, ViewportInteractor ) )
{
StartAction( EquippedAction, ViewportInteractor, CommandCDO->NeedsHoverLocation(), CommandCDO->GetUndoText() );
StartAction( EquippedAction, ViewportInteractor, EditCommand->NeedsHoverLocation(), EditCommand->GetUndoText() );
if( CommandCDO->NeedsDraggingInitiated() )
if( EditCommand->NeedsDraggingInitiated() )
{
bWantToStartMoving = true;
}
@@ -4909,13 +4909,9 @@ void FMeshEditorMode::MakeVRRadialMenuActionsMenu(FMenuBuilder& MenuBuilder, TSh
);
}
for( TObjectIterator<UMeshEditorCommand> CommandCDOIter( RF_NoFlags ); CommandCDOIter; ++CommandCDOIter )
for( UMeshEditorCommand* Command : MeshEditorCommands::Get() )
{
UMeshEditorCommand* CommandCDO = *CommandCDOIter;
if( !( CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract ) )
{
CommandCDO->AddToVRRadialMenuActionsMenu( *this, MenuBuilder, CommandList, FMeshEditorStyle::GetStyleSetName(), VRMode );
}
Command->AddToVRRadialMenuActionsMenu( *this, MenuBuilder, CommandList, FMeshEditorStyle::GetStyleSetName(), VRMode );
}
}

View File

@@ -197,7 +197,7 @@ protected:
virtual void DeselectAllMeshElements() override;
virtual void DeselectMeshElements( const TArray<FMeshElement>& MeshElementsToDeselect ) override;
virtual void DeselectMeshElements( const TMap<UEditableMesh*, TArray<FMeshElement>>& MeshElementsToDeselect ) override;
virtual void FindEdgeSplitUnderInteractor( UViewportInteractor* ViewportInteractor, const UEditableMesh* EditableMesh, const TArray<FMeshElement>& EdgeElements, TArray<float>& OutSplits ) override;
virtual bool FindEdgeSplitUnderInteractor( UViewportInteractor* ViewportInteractor, const UEditableMesh* EditableMesh, const TArray<FMeshElement>& EdgeElements, FEdgeID& OutClosestEdgeID, float& OutSplit ) override;
virtual class UViewportInteractor* GetActiveActionInteractor() override
{
return ActiveActionInteractor;

View File

@@ -52,8 +52,8 @@ public:
/** Commits all selected meshes */
virtual void CommitSelectedMeshes() = 0;
/** Given an interactor and a mesh, finds edges under the interactor along with their exact split position (progress along the edge) */
virtual void FindEdgeSplitUnderInteractor( class UViewportInteractor* ViewportInteractor, const UEditableMesh* EditableMesh, const TArray<FMeshElement>& EdgeElements, TArray<float>& OutSplits ) = 0;
/** Given an interactor and a mesh, finds edges under the interactor along with their exact split position (progress along the edge). Returns true if we found a split position. */
virtual bool FindEdgeSplitUnderInteractor( class UViewportInteractor* ViewportInteractor, const UEditableMesh* EditableMesh, const TArray<FMeshElement>& EdgeElements, FEdgeID& OutClosestEdgeID, float& OutSplit ) = 0;
/** When performing an interactive action that was initiated using an interactor, this is the interactor that was used. */
virtual class UViewportInteractor* GetActiveActionInteractor() = 0;

View File

@@ -251,3 +251,26 @@ public:
TSharedPtr<FUICommandInfo> AssignMaterial;
};
UCLASS()
class UMeshEditorCommandList : public UObject
{
GENERATED_BODY()
public:
void HarvestMeshEditorCommands();
/** All of the mesh editor commands that were registered at startup */
UPROPERTY()
TArray<UMeshEditorCommand*> MeshEditorCommands;
};
namespace MeshEditorCommands
{
extern const TArray<UMeshEditorCommand*>& Get();
}

View File

@@ -42,25 +42,13 @@ void UInsertEdgeLoopCommand::ApplyDuringDrag( IMeshEditorModeEditingContract& Me
const TArray<FMeshElement>& EdgeElements = MeshAndEdges.Value;
// Figure out where to add the loop along the edge
static TArray<float> Splits;
MeshEditorMode.FindEdgeSplitUnderInteractor( MeshEditorMode.GetActiveActionInteractor(), EditableMesh, EdgeElements, /* Out */ Splits );
FEdgeID ClosestEdgeID = FEdgeID::Invalid;
float Split = 0.0f;
const bool bFoundSplit = MeshEditorMode.FindEdgeSplitUnderInteractor( MeshEditorMode.GetActiveActionInteractor(), EditableMesh, EdgeElements, /* Out */ ClosestEdgeID, /* Out */ Split );
// Insert the edge loop
if( Splits.Num() > 0 )
if( bFoundSplit )
{
// @todo mesheditor edgeloop: Test code
if( false )
{
if( Splits[ 0 ] > 0.25f )
{
Splits.Insert( FMath::Max( Splits[ 0 ] - 0.2f, 0.0f ), 0 );
}
if( Splits.Last() < 0.75f )
{
Splits.Add( FMath::Min( Splits.Last() + 0.2f, 1.0f ) );
}
}
for( const FMeshElement& EdgeMeshElement : EdgeElements )
{
const FEdgeID EdgeID( EdgeMeshElement.ElementAddress.ElementID );
@@ -68,6 +56,9 @@ void UInsertEdgeLoopCommand::ApplyDuringDrag( IMeshEditorModeEditingContract& Me
static TArray<FEdgeID> NewEdgeIDs;
NewEdgeIDs.Reset();
static TArray<float> Splits; // @todo mesheditor edgeloop: Add support for inserting multiple splits at once!
Splits.Reset();
Splits.Add( Split );
EditableMesh->InsertEdgeLoop( EdgeID, Splits, /* Out */ NewEdgeIDs );
// Select all of the new edges that were created by inserting the loop

View File

@@ -26,6 +26,8 @@ bool USplitEdgeCommand::TryStartingToDrag( IMeshEditorModeEditingContract& MeshE
// Figure out what to split
MeshEditorMode.GetSelectedMeshesAndEdges( /* Out */ SplitEdgeMeshesAndEdgesToSplit );
SplitEdgeSplitList.Reset();
if( SplitEdgeMeshesAndEdgesToSplit.Num() > 0 )
{
for( auto& MeshAndEdges : SplitEdgeMeshesAndEdgesToSplit )
@@ -34,12 +36,15 @@ bool USplitEdgeCommand::TryStartingToDrag( IMeshEditorModeEditingContract& MeshE
const TArray<FMeshElement>& EdgeElements = MeshAndEdges.Value;
// Figure out where to split
MeshEditorMode.FindEdgeSplitUnderInteractor( ViewportInteractor, EditableMesh, EdgeElements, /* Out */ SplitEdgeSplitList );
FEdgeID ClosestEdgeID = FEdgeID::Invalid;
float Split = 0.0f;
const bool bFoundSplit = MeshEditorMode.FindEdgeSplitUnderInteractor( ViewportInteractor, EditableMesh, EdgeElements, /* Out */ ClosestEdgeID, /* Out */ Split );
// Split the edges
if( SplitEdgeSplitList.Num() > 0 )
if( bFoundSplit )
{
SplitEdgeSplitList.Add( Split );
bHaveEdge = true;
break;
}
}
}