Files
UnrealEngine/Engine/Plugins/Experimental/MeshModelingToolsetExp/Source/MeshModelingToolsExp/Public/MirrorTool.h
2025-05-18 13:04:45 +08:00

236 lines
7.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BaseGizmos/CombinedTransformGizmo.h"
#include "Changes/DynamicMeshChangeTarget.h"
#include "InteractiveToolBuilder.h"
#include "Mechanics/ConstructionPlaneMechanic.h"
#include "MeshOpPreviewHelpers.h"
#include "BaseTools/MultiSelectionMeshEditingTool.h"
#include "Selection/SelectClickedAction.h"
#include "ToolContextInterfaces.h"
#include "MirrorTool.generated.h"
#define UE_API MESHMODELINGTOOLSEXP_API
class UCreateMeshObjectTypeProperties;
class UOnAcceptHandleSourcesProperties;
UCLASS(MinimalAPI)
class UMirrorToolBuilder : public UMultiSelectionMeshEditingToolBuilder
{
GENERATED_BODY()
public:
UE_API virtual UMultiSelectionMeshEditingTool* CreateNewTool(const FToolBuilderState& SceneState) const override;
};
UENUM()
enum class EMirrorSaveMode : uint8
{
/** Save the results in place of the original input objects. */
InputObjects = 0,
/** Save the results as new objects. */
NewObjects = 1,
};
UENUM()
enum class EMirrorOperationMode : uint8
{
/** Append a mirrored version of the mesh to itself. */
MirrorAndAppend = 0,
/** Mirror the existing mesh. */
MirrorExisting = 1,
};
UCLASS(MinimalAPI)
class UMirrorToolProperties : public UInteractiveToolPropertySet
{
GENERATED_BODY()
public:
/** Mode of operation. */
UPROPERTY(EditAnywhere, Category = Options)
EMirrorOperationMode OperationMode = EMirrorOperationMode::MirrorAndAppend;
/** Cut off everything on the back side of the mirror plane before mirroring. */
UPROPERTY(EditAnywhere, Category = Options)
bool bCropAlongMirrorPlaneFirst = true;
/** Whether to locally simplify new edges created when cropping along the mirror plane. Will only simplify when doing so will not change the shape, UVs or PolyGroups. */
UPROPERTY(EditAnywhere, Category = Options, AdvancedDisplay, meta = (EditCondition = "bCropAlongMirrorPlaneFirst"))
bool bSimplifyAlongCrop = true;
/** Weld vertices that lie on the mirror plane. Vertices will not be welded if doing so would give an edge more than two faces, or if they are part of a face in the plane. */
UPROPERTY(EditAnywhere, Category = Options, meta = (
EditCondition = "OperationMode == EMirrorOperationMode::MirrorAndAppend", EditConditionHides))
bool bWeldVerticesOnMirrorPlane = true;
/** Distance (in unscaled mesh space) to allow a point to be from the plane and still consider it "on the mirror plane". */
UPROPERTY(EditAnywhere, Category = Options, meta = (
EditCondition = "OperationMode == EMirrorOperationMode::MirrorAndAppend && bWeldVerticesOnMirrorPlane", EditConditionHides,
UIMin = 0, UIMax = 0.01, ClampMin = 0, ClampMax = 10))
double PlaneTolerance = KINDA_SMALL_NUMBER;
/** When welding, whether to allow bowtie vertices to be created, or to duplicate the vertex. */
UPROPERTY(EditAnywhere, Category = Options, AdvancedDisplay, meta = (
EditCondition = "bWeldVerticesOnMirrorPlane && OperationMode == EMirrorOperationMode::MirrorAndAppend", EditConditionHides))
bool bAllowBowtieVertexCreation = false;
/** Whether to show the preview. */
UPROPERTY(EditAnywhere, Category = Options)
bool bShowPreview = true;
/** How to save the result. */
UPROPERTY(EditAnywhere, Category = OutputOptions)
EMirrorSaveMode WriteTo = EMirrorSaveMode::InputObjects;
};
UCLASS(MinimalAPI)
class UMirrorOperatorFactory : public UObject, public UE::Geometry::IDynamicMeshOperatorFactory
{
GENERATED_BODY()
public:
// IDynamicMeshOperatorFactory API
UE_API virtual TUniquePtr<UE::Geometry::FDynamicMeshOperator> MakeNewOperator() override;
UPROPERTY()
TObjectPtr<UMirrorTool> MirrorTool;
/** Index of the component within MirrorTool->ComponentTargets that this factory creates an operator for. */
int ComponentIndex;
};
UENUM()
enum class EMirrorToolAction
{
NoAction,
ShiftToCenter,
Left,
Right,
Up,
Down,
Forward,
Backward
};
UCLASS(MinimalAPI)
class UMirrorToolActionPropertySet : public UInteractiveToolPropertySet
{
GENERATED_BODY()
public:
TWeakObjectPtr<UMirrorTool> ParentTool;
void Initialize(UMirrorTool* ParentToolIn) { ParentTool = ParentToolIn; }
UE_API void PostAction(EMirrorToolAction Action);
/** Move the mirror plane to center of bounding box without changing its normal. */
UFUNCTION(CallInEditor, Category = RepositionPlane)
void ShiftToCenter() { PostAction(EMirrorToolAction::ShiftToCenter); }
/** If true the "Preset Mirror Directions" buttons only change the plane orientation, not location. */
UPROPERTY(EditAnywhere, Category = PresetMirrorDirections)
bool bButtonsOnlyChangeOrientation = false;
/** Move the mirror plane and adjust its normal to mirror entire selection leftward. */
UFUNCTION(CallInEditor, Category = PresetMirrorDirections, meta = (DisplayPriority = 1))
void Left() { PostAction(EMirrorToolAction::Left); }
/** Move the mirror plane and adjust its normal to mirror entire selection rightward. */
UFUNCTION(CallInEditor, Category = PresetMirrorDirections, meta = (DisplayPriority = 2))
void Right() { PostAction(EMirrorToolAction::Right); }
/** Move the mirror plane and adjust its normal to mirror entire selection upward. */
UFUNCTION(CallInEditor, Category = PresetMirrorDirections, meta = (DisplayPriority = 3))
void Up() { PostAction(EMirrorToolAction::Up); }
/** Move the mirror plane and adjust its normal to mirror entire selection downward. */
UFUNCTION(CallInEditor, Category = PresetMirrorDirections, meta = (DisplayPriority = 4))
void Down() { PostAction(EMirrorToolAction::Down); }
/** Move the mirror plane and adjust its normal to mirror entire selection forward. */
UFUNCTION(CallInEditor, Category = PresetMirrorDirections, meta = (DisplayPriority = 5))
void Forward() { PostAction(EMirrorToolAction::Forward); }
/** Move the mirror plane and adjust its normal to mirror entire selection backward. */
UFUNCTION(CallInEditor, Category = PresetMirrorDirections, meta = (DisplayPriority = 6))
void Backward() { PostAction(EMirrorToolAction::Backward); }
};
/** Tool for mirroring one or more meshes across a plane. */
UCLASS(MinimalAPI)
class UMirrorTool : public UMultiSelectionMeshEditingTool, public IModifierToggleBehaviorTarget
{
GENERATED_BODY()
public:
friend UMirrorOperatorFactory;
UE_API UMirrorTool();
UE_API virtual void Setup() override;
UE_API virtual void OnShutdown(EToolShutdownType ShutdownType) override;
UE_API virtual void OnTick(float DeltaTime) override;
UE_API virtual void Render(IToolsContextRenderAPI* RenderAPI) override;
virtual bool HasCancel() const override { return true; }
virtual bool HasAccept() const override { return true; }
UE_API virtual bool CanAccept() const override;
UE_API void RequestAction(EMirrorToolAction ActionType);
UE_API virtual void OnPropertyModified(UObject* PropertySet, FProperty* Property) override;
// IClickSequenceBehaviorTarget implementation
UE_API virtual void OnUpdateModifierState(int ModifierID, bool bIsOn) override;
protected:
UPROPERTY()
TObjectPtr<UMirrorToolProperties> Settings = nullptr;
UPROPERTY()
TObjectPtr<UCreateMeshObjectTypeProperties> OutputTypeProperties = nullptr;
UPROPERTY()
TObjectPtr<UOnAcceptHandleSourcesProperties> HandleSourcesProperties = nullptr;
UPROPERTY()
TObjectPtr<UMirrorToolActionPropertySet> ToolActions = nullptr;
UPROPERTY()
TArray<TObjectPtr<UDynamicMeshReplacementChangeTarget>> MeshesToMirror;
UPROPERTY()
TArray<TObjectPtr<UMeshOpPreviewWithBackgroundCompute>> Previews;
FVector3d MirrorPlaneOrigin = FVector3d::Zero();
FVector3d MirrorPlaneNormal = FVector3d::UnitZ();
UPROPERTY()
TObjectPtr<UConstructionPlaneMechanic> PlaneMechanic;
EMirrorToolAction PendingAction;
FBox CombinedBounds;
UE_API void ApplyAction(EMirrorToolAction ActionType);
UE_API void SetupPreviews();
UE_API void GenerateAsset(const TArray<FDynamicMeshOpResult>& Results);
private:
UE_API void CheckAndDisplayWarnings();
};
#undef UE_API