2020-06-23 18:40:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MirrorTool.h"
2021-06-02 15:58:00 -04:00
# include "ModelingObjectsCreationAPI.h"
2020-06-23 18:40:00 -04:00
# include "BaseBehaviors/KeyAsModifierInputBehavior.h"
# include "BaseBehaviors/SingleClickBehavior.h"
# include "CompositionOps/MirrorOp.h"
# include "Drawing/MeshDebugDrawing.h"
# include "DynamicMeshToMeshDescription.h"
# include "InteractiveToolManager.h"
# include "MeshDescriptionToDynamicMesh.h"
# include "Misc/MessageDialog.h"
# include "ToolBuilderUtil.h"
# include "ToolSetupUtil.h"
2021-03-11 11:40:03 -04:00
# include "TargetInterfaces/MaterialProvider.h"
# include "TargetInterfaces/MeshDescriptionCommitter.h"
# include "TargetInterfaces/MeshDescriptionProvider.h"
# include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
# include "ToolTargetManager.h"
2021-03-09 19:33:56 -04:00
# include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types)
using namespace UE : : Geometry ;
2020-06-23 18:40:00 -04:00
# define LOCTEXT_NAMESPACE "UMirrorTool"
//Tool builder functions
2021-03-11 11:40:03 -04:00
const FToolTargetTypeRequirements & UMirrorToolBuilder : : GetTargetRequirements ( ) const
{
static FToolTargetTypeRequirements TypeRequirements ( {
UMaterialProvider : : StaticClass ( ) ,
UMeshDescriptionCommitter : : StaticClass ( ) ,
UMeshDescriptionProvider : : StaticClass ( ) ,
2021-06-22 11:54:55 -04:00
UPrimitiveComponentBackedTarget : : StaticClass ( )
2021-03-11 11:40:03 -04:00
} ) ;
return TypeRequirements ;
}
2020-06-23 18:40:00 -04:00
bool UMirrorToolBuilder : : CanBuildTool ( const FToolBuilderState & SceneState ) const
{
2021-06-02 15:58:00 -04:00
return SceneState . TargetManager - > CountSelectedAndTargetable ( SceneState , GetTargetRequirements ( ) ) > 0 ;
2020-06-23 18:40:00 -04:00
}
UInteractiveTool * UMirrorToolBuilder : : BuildTool ( const FToolBuilderState & SceneState ) const
{
UMirrorTool * NewTool = NewObject < UMirrorTool > ( SceneState . ToolManager ) ;
2021-03-11 11:40:03 -04:00
TArray < TObjectPtr < UToolTarget > > Targets = SceneState . TargetManager - > BuildAllSelectedTargetable ( SceneState , GetTargetRequirements ( ) ) ;
NewTool - > SetTargets ( MoveTemp ( Targets ) ) ;
2020-06-23 18:40:00 -04:00
NewTool - > SetWorld ( SceneState . World ) ;
return NewTool ;
}
// Operator factory
TUniquePtr < FDynamicMeshOperator > UMirrorOperatorFactory : : MakeNewOperator ( )
{
TUniquePtr < FMirrorOp > MirrorOp = MakeUnique < FMirrorOp > ( ) ;
// Set up inputs and settings
MirrorOp - > OriginalMesh = MirrorTool - > MeshesToMirror [ ComponentIndex ] - > GetMesh ( ) ;
MirrorOp - > bAppendToOriginal = MirrorTool - > Settings - > OperationMode = = EMirrorOperationMode : : MirrorAndAppend ;
MirrorOp - > bCropFirst = MirrorTool - > Settings - > bCropAlongMirrorPlaneFirst ;
MirrorOp - > bWeldAlongPlane = MirrorTool - > Settings - > bWeldVerticesOnMirrorPlane ;
MirrorOp - > bAllowBowtieVertexCreation = MirrorTool - > Settings - > bAllowBowtieVertexCreation ;
2021-03-11 11:40:03 -04:00
FTransform LocalToWorld = MirrorTool - > TargetComponentInterface ( ComponentIndex ) - > GetWorldTransform ( ) ;
2020-06-23 18:40:00 -04:00
MirrorOp - > SetTransform ( LocalToWorld ) ;
// We also need WorldToLocal. Threshold the LocalToWorld scaling transform so we can get the inverse.
FVector LocalToWorldScale = LocalToWorld . GetScale3D ( ) ;
for ( int i = 0 ; i < 3 ; i + + )
{
float DimScale = FMathf : : Abs ( LocalToWorldScale [ i ] ) ;
float Tolerance = KINDA_SMALL_NUMBER ;
if ( DimScale < Tolerance )
{
LocalToWorldScale [ i ] = Tolerance * FMathf : : SignNonZero ( LocalToWorldScale [ i ] ) ;
}
}
LocalToWorld . SetScale3D ( LocalToWorldScale ) ;
2021-03-18 12:46:27 -04:00
UE : : Geometry : : FTransform3d WorldToLocal = UE : : Geometry : : FTransform3d ( LocalToWorld ) . Inverse ( ) ;
2020-06-23 18:40:00 -04:00
// Now we can get the plane parameters in local space.
MirrorOp - > LocalPlaneOrigin = WorldToLocal . TransformPosition ( MirrorTool - > MirrorPlaneOrigin ) ; ;
FVector3d WorldNormal = MirrorTool - > MirrorPlaneNormal ;
MirrorOp - > LocalPlaneNormal = WorldToLocal . TransformNormal ( MirrorTool - > MirrorPlaneNormal ) ;
return MirrorOp ;
}
// Tool property functions
void UMirrorToolActionPropertySet : : PostAction ( EMirrorToolAction Action )
{
if ( ParentTool . IsValid ( ) )
{
ParentTool - > RequestAction ( Action ) ;
}
}
// Tool itself
UMirrorTool : : UMirrorTool ( )
{
}
bool UMirrorTool : : CanAccept ( ) const
{
for ( UMeshOpPreviewWithBackgroundCompute * Preview : Previews )
{
if ( ! Preview - > HaveValidResult ( ) )
{
return false ;
}
}
2020-11-24 18:42:39 -04:00
return Super : : CanAccept ( ) ;
2020-06-23 18:40:00 -04:00
}
void UMirrorTool : : SetWorld ( UWorld * World )
{
TargetWorld = World ;
}
void UMirrorTool : : OnPropertyModified ( UObject * PropertySet , FProperty * Property )
{
// Editing the "show preview" option changes whether we need to be displaying the preview or the original mesh.
if ( Property & & ( Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UMirrorToolProperties , bShowPreview ) ) )
{
2021-03-11 11:40:03 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2020-06-23 18:40:00 -04:00
{
2021-03-11 11:40:03 -04:00
TargetComponentInterface ( ComponentIdx ) - > SetOwnerVisibility ( ! Settings - > bShowPreview ) ;
2020-06-23 18:40:00 -04:00
}
for ( UMeshOpPreviewWithBackgroundCompute * Preview : Previews )
{
Preview - > SetVisibility ( Settings - > bShowPreview ) ;
}
}
// Regardless of what changed, update the previews.
for ( UMeshOpPreviewWithBackgroundCompute * Preview : Previews )
{
Preview - > InvalidateResult ( ) ;
}
}
void UMirrorTool : : OnTick ( float DeltaTime )
{
// Deal with any buttons that may have been clicked
if ( PendingAction ! = EMirrorToolAction : : NoAction )
{
ApplyAction ( PendingAction ) ;
PendingAction = EMirrorToolAction : : NoAction ;
}
if ( PlaneMechanic ! = nullptr )
{
// Update snapping behavior based on modifier key.
PlaneMechanic - > SetEnableGridSnaping ( Settings - > bSnapToWorldGrid ^ bSnappingToggle ) ;
PlaneMechanic - > Tick ( DeltaTime ) ;
}
for ( UMeshOpPreviewWithBackgroundCompute * Preview : Previews )
{
Preview - > Tick ( DeltaTime ) ;
}
}
void UMirrorTool : : Render ( IToolsContextRenderAPI * RenderAPI )
{
// Have the plane draw itself.
PlaneMechanic - > Render ( RenderAPI ) ;
}
void UMirrorTool : : Setup ( )
{
UInteractiveTool : : Setup ( ) ;
2021-02-08 17:02:09 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Mirror " ) ) ;
2020-06-23 18:40:00 -04:00
GetToolManager ( ) - > DisplayMessage (
2020-10-22 19:19:16 -04:00
LOCTEXT ( " OnStartMirrorTool " , " Mirror one or more meshes across a plane. Grid snapping behavior is swapped while the shift key is down. The plane can be set by using the preset buttons, moving the gizmo, or ctrl+clicking on a spot on the original mesh. " ) ,
2020-06-23 18:40:00 -04:00
EToolMessageLevel : : UserNotification ) ;
// Set up the properties
Settings = NewObject < UMirrorToolProperties > ( this , TEXT ( " Mirror Tool Settings " ) ) ;
Settings - > RestoreProperties ( this ) ;
AddToolPropertySource ( Settings ) ;
ToolActions = NewObject < UMirrorToolActionPropertySet > ( this ) ;
ToolActions - > Initialize ( this ) ;
AddToolPropertySource ( ToolActions ) ;
2021-03-11 11:40:03 -04:00
CheckAndDisplayWarnings ( ) ;
2020-06-23 18:40:00 -04:00
// Fill in the MeshesToMirror array with suitably converted meshes.
2021-03-11 11:40:03 -04:00
for ( int i = 0 ; i < Targets . Num ( ) ; i + + )
2020-06-23 18:40:00 -04:00
{
2021-03-11 11:40:03 -04:00
IPrimitiveComponentBackedTarget * TargetComponent = TargetComponentInterface ( i ) ;
IMeshDescriptionProvider * TargetMeshProvider = TargetMeshProviderInterface ( i ) ;
2020-06-23 18:40:00 -04:00
// Convert into dynamic mesh
2021-02-17 11:50:23 -04:00
TSharedPtr < FDynamicMesh3 , ESPMode : : ThreadSafe > DynamicMesh = MakeShared < FDynamicMesh3 , ESPMode : : ThreadSafe > ( ) ;
2020-06-23 18:40:00 -04:00
FMeshDescriptionToDynamicMesh Converter ;
2021-03-11 11:40:03 -04:00
Converter . Convert ( TargetMeshProvider - > GetMeshDescription ( ) , * DynamicMesh ) ;
2020-06-23 18:40:00 -04:00
// Wrap the dynamic mesh in a replacement change target
UDynamicMeshReplacementChangeTarget * WrappedTarget = MeshesToMirror . Add_GetRef ( NewObject < UDynamicMeshReplacementChangeTarget > ( ) ) ;
// Set callbacks so previews are invalidated on undo/redo changing the meshes
WrappedTarget - > SetMesh ( DynamicMesh ) ;
WrappedTarget - > OnMeshChanged . AddLambda ( [ this , i ] ( ) { Previews [ i ] - > InvalidateResult ( ) ; } ) ;
}
// Set the visibility of the StaticMeshComponents depending on whether we are showing them or the preview.
2021-03-11 11:40:03 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2020-06-23 18:40:00 -04:00
{
2021-03-11 11:40:03 -04:00
TargetComponentInterface ( ComponentIdx ) - > SetOwnerVisibility ( ! Settings - > bShowPreview ) ;
2020-06-23 18:40:00 -04:00
}
// Initialize the PreviewMesh and BackgroundCompute objects
SetupPreviews ( ) ;
// Update the bounding box of the meshes.
CombinedBounds . Init ( ) ;
2021-03-11 11:40:03 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2020-06-23 18:40:00 -04:00
{
FVector ComponentOrigin , ComponentExtents ;
2021-03-11 11:40:03 -04:00
TargetComponentInterface ( ComponentIdx ) - > GetOwnerActor ( ) - > GetActorBounds ( false , ComponentOrigin , ComponentExtents ) ;
2020-06-23 18:40:00 -04:00
CombinedBounds + = FBox : : BuildAABB ( ComponentOrigin , ComponentExtents ) ;
}
// Set the initial mirror plane. We want the plane to start in the middle if we're doing a simple
// mirror (i.e., not appending, and not cropping). Otherwise, we want the plane to start to one side.
2021-03-30 21:25:22 -04:00
MirrorPlaneOrigin = ( FVector3d ) CombinedBounds . GetCenter ( ) ;
2020-06-23 18:40:00 -04:00
MirrorPlaneNormal = FVector3d ( 0 , - 1 , 0 ) ;
if ( Settings - > OperationMode = = EMirrorOperationMode : : MirrorAndAppend | | Settings - > bCropAlongMirrorPlaneFirst )
{
MirrorPlaneOrigin . Y = CombinedBounds . Min . Y ;
}
// Set up the mirror plane mechanic, which manages the gizmo
PlaneMechanic = NewObject < UConstructionPlaneMechanic > ( this ) ;
PlaneMechanic - > Setup ( this ) ;
PlaneMechanic - > Initialize ( TargetWorld , FFrame3d ( MirrorPlaneOrigin , MirrorPlaneNormal ) ) ;
// Have the plane mechanic update things properly
PlaneMechanic - > OnPlaneChanged . AddLambda ( [ this ] ( ) {
MirrorPlaneNormal = PlaneMechanic - > Plane . Rotation . AxisZ ( ) ;
MirrorPlaneOrigin = PlaneMechanic - > Plane . Origin ;
for ( UMeshOpPreviewWithBackgroundCompute * Preview : Previews )
{
Preview - > InvalidateResult ( ) ;
}
} ) ;
2020-10-29 13:38:15 -04:00
// Modify the Ctrl+click set plane behavior to respond to our CtrlClickBehavior property
PlaneMechanic - > SetPlaneCtrlClickBehaviorTarget - > OnClickedPositionFunc = [ this ] ( const FHitResult & Hit )
2020-06-23 18:40:00 -04:00
{
bool bIgnoreNormal = ( Settings - > CtrlClickBehavior = = EMirrorCtrlClickBehavior : : Reposition ) ;
2021-03-30 21:25:22 -04:00
PlaneMechanic - > SetDrawPlaneFromWorldPos ( ( FVector3d ) Hit . ImpactPoint , ( FVector3d ) Hit . ImpactNormal , bIgnoreNormal ) ;
2020-06-23 18:40:00 -04:00
} ;
2020-10-29 13:38:15 -04:00
// Also include the original components in the ctrl+click hit testing even though we made them
// invisible, since we want to be able to reposition the plane onto the original mesh.
2021-03-11 11:40:03 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2020-10-29 13:38:15 -04:00
{
2021-03-11 11:40:03 -04:00
IPrimitiveComponentBackedTarget * TargetComponent = TargetComponentInterface ( ComponentIdx ) ;
PlaneMechanic - > SetPlaneCtrlClickBehaviorTarget - > InvisibleComponentsToHitTest . Add ( TargetComponent - > GetOwnerComponent ( ) ) ;
2020-10-29 13:38:15 -04:00
}
2020-06-23 18:40:00 -04:00
// Add modifier button for snapping
UKeyAsModifierInputBehavior * SnapToggleBehavior = NewObject < UKeyAsModifierInputBehavior > ( ) ;
2021-03-18 16:02:18 -04:00
SnapToggleBehavior - > Initialize ( this , SnappingToggleModifierId , FInputDeviceState : : IsShiftKeyDown ) ;
2020-06-23 18:40:00 -04:00
AddInputBehavior ( SnapToggleBehavior ) ;
// Start the preview calculations
for ( UMeshOpPreviewWithBackgroundCompute * Preview : Previews )
{
Preview - > InvalidateResult ( ) ;
}
}
void UMirrorTool : : SetupPreviews ( )
{
// Create a preview (with an op) for each selected component.
int32 NumMeshes = MeshesToMirror . Num ( ) ;
for ( int32 PreviewIndex = 0 ; PreviewIndex < NumMeshes ; + + PreviewIndex )
{
UMirrorOperatorFactory * MirrorOpCreator = NewObject < UMirrorOperatorFactory > ( ) ;
MirrorOpCreator - > MirrorTool = this ;
MirrorOpCreator - > ComponentIndex = PreviewIndex ;
UMeshOpPreviewWithBackgroundCompute * Preview = Previews . Add_GetRef (
NewObject < UMeshOpPreviewWithBackgroundCompute > ( MirrorOpCreator , " Preview " ) ) ;
Preview - > Setup ( TargetWorld , MirrorOpCreator ) ;
2021-06-11 22:39:18 -04:00
Preview - > PreviewMesh - > SetTangentsMode ( EDynamicMeshComponentTangentsMode : : AutoCalculated ) ;
2020-06-23 18:40:00 -04:00
FComponentMaterialSet MaterialSet ;
2021-03-11 11:40:03 -04:00
TargetMaterialInterface ( PreviewIndex ) - > GetMaterialSet ( MaterialSet ) ;
2020-06-23 18:40:00 -04:00
Preview - > ConfigureMaterials ( MaterialSet . Materials , ToolSetupUtil : : GetDefaultWorkingMaterial ( GetToolManager ( ) ) ) ;
// Set initial preview to unprocessed mesh, so that things don't disappear initially
Preview - > PreviewMesh - > UpdatePreview ( MeshesToMirror [ PreviewIndex ] - > GetMesh ( ) . Get ( ) ) ;
2021-03-11 11:40:03 -04:00
Preview - > PreviewMesh - > SetTransform ( TargetComponentInterface ( PreviewIndex ) - > GetWorldTransform ( ) ) ;
2020-06-23 18:40:00 -04:00
Preview - > SetVisibility ( Settings - > bShowPreview ) ;
}
}
2021-03-11 11:40:03 -04:00
void UMirrorTool : : CheckAndDisplayWarnings ( )
2020-06-23 18:40:00 -04:00
{
// We can have more than one warning, which makes this a bit more work.
FText SameSourceWarning ;
FText NonUniformScaleWarning ;
// See if any of the selected components have the same source.
2021-03-11 11:40:03 -04:00
TArray < int32 > MapToFirstOccurrences ;
bool bAnyHaveSameSource = GetMapToSharedSourceData ( MapToFirstOccurrences ) ;
2020-06-23 18:40:00 -04:00
if ( bAnyHaveSameSource )
{
2020-10-22 19:19:16 -04:00
SameSourceWarning = LOCTEXT ( " MirrorMultipleAssetsWithSameSource " , " WARNING: Multiple meshes in your selection use the same source asset! Only the \" Create New Assets \" save mode is supported. " ) ;
2020-06-23 18:40:00 -04:00
// We could forcefully set the save mode to CreateNewAssets, but the setting will persist on new invocations
// of the tool, which may surprise the user. So, it's up to them to set it.
}
// See if any of the selected components have a nonuniform scaling transform.
2021-03-11 11:40:03 -04:00
IPrimitiveComponentBackedTarget * NonUniformScalingTarget = nullptr ;
for ( int32 i = 0 ; i < Targets . Num ( ) ; + + i )
2020-06-23 18:40:00 -04:00
{
2021-03-11 11:40:03 -04:00
FVector Scaling = TargetComponentInterface ( i ) - > GetWorldTransform ( ) . GetScale3D ( ) ;
2020-06-23 18:40:00 -04:00
if ( Scaling . X ! = Scaling . Y | | Scaling . Y ! = Scaling . Z )
{
2021-03-11 11:40:03 -04:00
NonUniformScalingTarget = TargetComponentInterface ( i ) ;
2020-06-23 18:40:00 -04:00
break ;
}
}
if ( NonUniformScalingTarget )
{
2020-10-22 19:19:16 -04:00
NonUniformScaleWarning = FText : : Format (
LOCTEXT ( " MirrorNonUniformScaledAsset " , " WARNING: The item \" {0} \" has a non-uniform scaling transform. This is not supported because mirroring acts on the underlying mesh, and mirroring is not commutative with non-uniform scaling. Consider deforming the mesh rather than scaling it non-uniformly. " ) ,
2020-06-23 18:40:00 -04:00
FText : : FromString ( NonUniformScalingTarget - > GetOwnerActor ( ) - > GetName ( ) ) ) ;
}
if ( bAnyHaveSameSource & & NonUniformScalingTarget )
{
// Concatenates the two warnings with an extra line in between.
2021-03-11 11:40:03 -04:00
GetToolManager ( ) - > DisplayMessage ( FText : : Format ( LOCTEXT ( " CombinedWarnings " , " {0} \n \n {1} " ) ,
2020-06-23 18:40:00 -04:00
SameSourceWarning , NonUniformScaleWarning ) , EToolMessageLevel : : UserWarning ) ;
}
else if ( bAnyHaveSameSource )
{
2021-03-11 11:40:03 -04:00
GetToolManager ( ) - > DisplayMessage ( SameSourceWarning , EToolMessageLevel : : UserWarning ) ;
2020-06-23 18:40:00 -04:00
}
else if ( NonUniformScalingTarget )
{
2021-03-11 11:40:03 -04:00
GetToolManager ( ) - > DisplayMessage ( NonUniformScaleWarning , EToolMessageLevel : : UserWarning ) ;
2020-06-23 18:40:00 -04:00
}
}
void UMirrorTool : : Shutdown ( EToolShutdownType ShutdownType )
{
Settings - > SaveProperties ( this ) ;
PlaneMechanic - > Shutdown ( ) ;
// Restore (unhide) the source meshes
2021-03-11 11:40:03 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2020-06-23 18:40:00 -04:00
{
2021-03-11 11:40:03 -04:00
TargetComponentInterface ( ComponentIdx ) - > SetOwnerVisibility ( true ) ;
2020-06-23 18:40:00 -04:00
}
// Swap in results, if appropriate
if ( ShutdownType = = EToolShutdownType : : Accept )
{
// Gather results
TArray < FDynamicMeshOpResult > Results ;
for ( UMeshOpPreviewWithBackgroundCompute * Preview : Previews )
{
Results . Emplace ( Preview - > Shutdown ( ) ) ;
}
// Convert to output. This will also edit the selection.
GenerateAsset ( Results ) ;
}
else
{
for ( UMeshOpPreviewWithBackgroundCompute * Preview : Previews )
{
Preview - > Cancel ( ) ;
}
}
}
void UMirrorTool : : GenerateAsset ( const TArray < FDynamicMeshOpResult > & Results )
{
if ( Results . Num ( ) = = 0 )
{
return ;
}
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " MirrorToolTransactionName " , " Mirror Tool " ) ) ;
ensure ( Results . Num ( ) > 0 ) ;
int32 NumSourceMeshes = MeshesToMirror . Num ( ) ;
// check if we entirely cut away any meshes
bool bWantToDestroy = false ;
for ( int OrigMeshIdx = 0 ; OrigMeshIdx < NumSourceMeshes ; OrigMeshIdx + + )
{
if ( Results [ OrigMeshIdx ] . Mesh - > TriangleCount ( ) = = 0 )
{
bWantToDestroy = true ;
break ;
}
}
// if so ask user what to do
if ( bWantToDestroy )
{
FText Title = LOCTEXT ( " MirrorDestroyTitle " , " Delete mesh components? " ) ;
EAppReturnType : : Type Ret = FMessageDialog : : Open ( EAppMsgType : : YesNo ,
LOCTEXT ( " PlaneCutDestroyQuestion " , " The mirror plane cropping has entirely cut away at least one mesh. Actually destroy these mesh components? " ) , & Title ) ;
if ( Ret = = EAppReturnType : : No )
{
bWantToDestroy = false ;
}
}
// Properly deal with each result, setting up the selection at the same time.
FSelectedOjectsChangeList NewSelection ;
NewSelection . ModificationType = ESelectedObjectsModificationType : : Replace ;
for ( int OrigMeshIdx = 0 ; OrigMeshIdx < NumSourceMeshes ; OrigMeshIdx + + )
{
2021-03-11 11:40:03 -04:00
IPrimitiveComponentBackedTarget * TargetComponent = TargetComponentInterface ( OrigMeshIdx ) ;
2020-06-23 18:40:00 -04:00
FDynamicMesh3 * Mesh = Results [ OrigMeshIdx ] . Mesh . Get ( ) ;
check ( Mesh ! = nullptr ) ;
if ( Mesh - > TriangleCount ( ) = = 0 )
{
if ( bWantToDestroy )
{
2021-03-11 11:40:03 -04:00
TargetComponent - > GetOwnerComponent ( ) - > DestroyComponent ( ) ;
2020-06-23 18:40:00 -04:00
}
continue ;
}
else if ( Settings - > SaveMode = = EMirrorSaveMode : : UpdateAssets )
{
2021-03-11 11:40:03 -04:00
NewSelection . Actors . Add ( TargetComponent - > GetOwnerActor ( ) ) ;
2020-06-23 18:40:00 -04:00
2021-03-11 11:40:03 -04:00
TargetMeshCommitterInterface ( OrigMeshIdx ) - > CommitMeshDescription ( [ & Mesh ] ( const IMeshDescriptionCommitter : : FCommitterParams & CommitParams )
2020-06-23 18:40:00 -04:00
{
FDynamicMeshToMeshDescription Converter ;
2021-03-11 11:40:03 -04:00
Converter . Convert ( Mesh , * CommitParams . MeshDescriptionOut ) ;
2020-06-23 18:40:00 -04:00
} ) ;
}
else
{
// Build array of materials from the original.
TArray < UMaterialInterface * > Materials ;
2021-03-11 11:40:03 -04:00
IMaterialProvider * TargetMaterial = TargetMaterialInterface ( OrigMeshIdx ) ;
for ( int MaterialIdx = 0 , NumMaterials = TargetMaterial - > GetNumMaterials ( ) ; MaterialIdx < NumMaterials ; MaterialIdx + + )
2020-06-23 18:40:00 -04:00
{
2021-03-11 11:40:03 -04:00
Materials . Add ( TargetMaterial - > GetMaterial ( MaterialIdx ) ) ;
2020-06-23 18:40:00 -04:00
}
2021-06-02 15:58:00 -04:00
FCreateMeshObjectParams NewMeshObjectParams ;
NewMeshObjectParams . TargetWorld = TargetWorld ;
NewMeshObjectParams . Transform = ( FTransform ) Results [ OrigMeshIdx ] . Transform ;
NewMeshObjectParams . BaseName = TEXT ( " Mirror " ) ;
NewMeshObjectParams . Materials = Materials ;
NewMeshObjectParams . SetMesh ( Mesh ) ;
FCreateMeshObjectResult Result = UE : : Modeling : : CreateMeshObject ( GetToolManager ( ) , MoveTemp ( NewMeshObjectParams ) ) ;
if ( Result . IsOK ( ) & & Result . NewActor ! = nullptr )
2020-06-23 18:40:00 -04:00
{
2021-06-02 15:58:00 -04:00
NewSelection . Actors . Add ( Result . NewActor ) ;
2020-06-23 18:40:00 -04:00
}
// Remove the original actor
2021-03-11 11:40:03 -04:00
TargetComponent - > GetOwnerComponent ( ) - > DestroyComponent ( ) ;
2020-06-23 18:40:00 -04:00
}
}
// Update the selection
if ( NewSelection . Actors . Num ( ) > 0 )
{
GetToolManager ( ) - > RequestSelectionChange ( NewSelection ) ;
}
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
// Action support
void UMirrorTool : : RequestAction ( EMirrorToolAction ActionType )
{
if ( PendingAction = = EMirrorToolAction : : NoAction )
{
PendingAction = ActionType ;
}
}
void UMirrorTool : : ApplyAction ( EMirrorToolAction ActionType )
{
2021-03-30 21:25:22 -04:00
FVector3d ShiftedPlaneOrigin = ( FVector3d ) CombinedBounds . GetCenter ( ) ;
2020-06-23 18:40:00 -04:00
if ( ActionType = = EMirrorToolAction : : ShiftToCenter )
{
// We keep the same orientation here
PlaneMechanic - > SetDrawPlaneFromWorldPos ( ShiftedPlaneOrigin , FVector3d ( ) , true ) ;
}
else
{
// We still start from the center, but adjust one of the coordinates and set direction.
FVector3d DirectionVector ;
switch ( ActionType )
{
case EMirrorToolAction : : Left :
ShiftedPlaneOrigin . Y = CombinedBounds . Min . Y ;
2021-03-30 21:25:22 -04:00
DirectionVector = FVector3d ( 0 , - 1.0 , 0 ) ;
2020-06-23 18:40:00 -04:00
break ;
case EMirrorToolAction : : Right :
ShiftedPlaneOrigin . Y = CombinedBounds . Max . Y ;
2021-03-30 21:25:22 -04:00
DirectionVector = FVector3d ( 0 , 1.0 , 0 ) ;
2020-06-23 18:40:00 -04:00
break ;
case EMirrorToolAction : : Up :
ShiftedPlaneOrigin . Z = CombinedBounds . Max . Z ;
2021-03-30 21:25:22 -04:00
DirectionVector = FVector3d ( 0 , 0 , 1.0 ) ;
2020-06-23 18:40:00 -04:00
break ;
case EMirrorToolAction : : Down :
ShiftedPlaneOrigin . Z = CombinedBounds . Min . Z ;
2021-03-30 21:25:22 -04:00
DirectionVector = FVector3d ( 0 , 0 , - 1.0 ) ;
2020-06-23 18:40:00 -04:00
break ;
case EMirrorToolAction : : Forward :
ShiftedPlaneOrigin . X = CombinedBounds . Max . X ;
2021-03-30 21:25:22 -04:00
DirectionVector = FVector3d ( 1.0 , 0 , 0 ) ;
2020-06-23 18:40:00 -04:00
break ;
case EMirrorToolAction : : Backward :
ShiftedPlaneOrigin . X = CombinedBounds . Min . X ;
2021-03-30 21:25:22 -04:00
DirectionVector = FVector3d ( - 1.0 , 0 , 0 ) ;
2020-06-23 18:40:00 -04:00
break ;
}
// The user can optionally have the button change the direction only
if ( Settings - > bButtonsOnlyChangeOrientation )
{
ShiftedPlaneOrigin = MirrorPlaneOrigin ; // Keeps the same
}
PlaneMechanic - > SetDrawPlaneFromWorldPos ( ShiftedPlaneOrigin , DirectionVector , false ) ;
}
}
void UMirrorTool : : OnUpdateModifierState ( int ModifierID , bool bIsOn )
{
if ( ModifierID = = SnappingToggleModifierId )
{
bSnappingToggle = bIsOn ;
}
}
# undef LOCTEXT_NAMESPACE