816 lines
31 KiB
C++
816 lines
31 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
|
|
#include "Changes/MeshVertexChange.h" // IMeshVertexCommandChangeTarget
|
|
#include "DynamicMesh/DynamicMeshAABBTree3.h"
|
|
#include "DynamicMesh/DynamicMeshChangeTracker.h" // FDynamicMeshChange for TUniquePtr
|
|
#include "InteractiveToolActivity.h" // IToolActivityHost
|
|
#include "InteractiveToolBuilder.h"
|
|
#include "InteractiveToolQueryInterfaces.h" // IInteractiveToolNestedAcceptCancelAPI
|
|
#include "Internationalization/Text.h"
|
|
#include "Operations/GroupTopologyDeformer.h"
|
|
#include "BaseTools/SingleTargetWithSelectionTool.h"
|
|
|
|
#include "GeometryBase.h"
|
|
|
|
#include "EditMeshPolygonsTool.generated.h"
|
|
|
|
#define UE_API MESHMODELINGTOOLS_API
|
|
|
|
PREDECLARE_GEOMETRY(class FGroupTopology);
|
|
PREDECLARE_GEOMETRY(struct FGroupTopologySelection);
|
|
PREDECLARE_GEOMETRY(class FDynamicMeshChangeTracker);
|
|
|
|
struct FSlateBrush;
|
|
class UCombinedTransformGizmo;
|
|
class UDragAlignmentMechanic;
|
|
class UMeshOpPreviewWithBackgroundCompute;
|
|
class FMeshVertexChangeBuilder;
|
|
class UEditMeshPolygonsTool;
|
|
class UPolyEditActivityContext;
|
|
class UPolyEditInsertEdgeActivity;
|
|
class UPolyEditInsertEdgeLoopActivity;
|
|
class UPolyEditExtrudeActivity;
|
|
class UPolyEditInsetOutsetActivity;
|
|
class UPolyEditCutFacesActivity;
|
|
class UPolyEditPlanarProjectionUVActivity;
|
|
class UPolyEditBevelEdgeActivity;
|
|
class UPolyEditExtrudeEdgeActivity;
|
|
class UPolygonSelectionMechanic;
|
|
class UTransformProxy;
|
|
|
|
|
|
/**
|
|
* ToolBuilder
|
|
*/
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsToolBuilder : public USingleTargetWithSelectionToolBuilder
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
bool bTriangleMode = false;
|
|
|
|
UE_API virtual USingleTargetWithSelectionTool* CreateNewTool(const FToolBuilderState& SceneState) const override;
|
|
UE_API virtual void InitializeNewTool(USingleTargetWithSelectionTool* Tool, const FToolBuilderState& SceneState) const override;
|
|
|
|
virtual bool RequiresInputSelection() const override { return false; }
|
|
};
|
|
|
|
|
|
UENUM()
|
|
enum class ELocalFrameMode
|
|
{
|
|
FromObject,
|
|
FromGeometry
|
|
};
|
|
|
|
|
|
/**
|
|
* These are properties that do not get enabled/disabled based on the action
|
|
*/
|
|
UCLASS(MinimalAPI)
|
|
class UPolyEditCommonProperties : public UInteractiveToolPropertySet
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UPROPERTY(EditAnywhere, Category = Options)
|
|
bool bShowWireframe = false;
|
|
|
|
UPROPERTY(EditAnywhere, Category = Options)
|
|
bool bShowSelectableCorners = true;
|
|
|
|
/** When true, allows the transform gizmo to be rendered */
|
|
UPROPERTY(EditAnywhere, Category = Gizmo)
|
|
bool bGizmoVisible = true;
|
|
|
|
/** Determines whether, on selection changes, the gizmo's rotation is taken from the object transform, or from the geometry
|
|
elements selected. Only relevant with a local coordinate system and when rotation is not locked. */
|
|
UPROPERTY(EditAnywhere, Category = Gizmo, meta = (HideEditConditionToggle, EditCondition = "bLocalCoordSystem && !bLockRotation"))
|
|
ELocalFrameMode LocalFrameMode = ELocalFrameMode::FromGeometry;
|
|
|
|
/** When true, keeps rotation of gizmo constant through selection changes and manipulations
|
|
(but not middle-click repositions). Only active with a local coordinate system.*/
|
|
UPROPERTY(EditAnywhere, Category = Gizmo, meta = (HideEditConditionToggle, EditCondition = "bLocalCoordSystem"))
|
|
bool bLockRotation = false;
|
|
|
|
/** This gets updated internally so that properties can respond to whether the coordinate system is set to local or global */
|
|
UPROPERTY()
|
|
bool bLocalCoordSystem = true;
|
|
};
|
|
|
|
|
|
UENUM()
|
|
enum class EEditMeshPolygonsToolActions
|
|
{
|
|
NoAction,
|
|
AcceptCurrent,
|
|
CancelCurrent,
|
|
Extrude,
|
|
PushPull,
|
|
Offset,
|
|
Inset,
|
|
Outset,
|
|
BevelFaces,
|
|
InsertEdge,
|
|
InsertEdgeLoop,
|
|
Complete,
|
|
|
|
PlaneCut,
|
|
Merge,
|
|
Delete,
|
|
CutFaces,
|
|
RecalculateNormals,
|
|
FlipNormals,
|
|
Retriangulate,
|
|
Decompose,
|
|
Disconnect,
|
|
Duplicate,
|
|
|
|
CollapseEdge,
|
|
WeldEdges,
|
|
WeldEdgesCentered,
|
|
StraightenEdge,
|
|
FillHole,
|
|
BridgeEdges,
|
|
ExtrudeEdges,
|
|
BevelEdges,
|
|
SimplifyAlongEdges,
|
|
|
|
PlanarProjectionUV,
|
|
|
|
SimplifyByGroups,
|
|
RegenerateExtraCorners,
|
|
|
|
// triangle-specific edits
|
|
PokeSingleFace,
|
|
SplitSingleEdge,
|
|
FlipSingleEdge,
|
|
CollapseSingleEdge,
|
|
|
|
// for external use
|
|
BevelAuto
|
|
};
|
|
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsActionModeToolBuilder : public UEditMeshPolygonsToolBuilder
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
EEditMeshPolygonsToolActions StartupAction = EEditMeshPolygonsToolActions::Extrude;
|
|
|
|
UE_API virtual bool CanBuildTool(const FToolBuilderState& SceneState) const override;
|
|
UE_API virtual void InitializeNewTool(USingleTargetWithSelectionTool* Tool, const FToolBuilderState& SceneState) const override;
|
|
};
|
|
|
|
UENUM()
|
|
enum class EEditMeshPolygonsToolSelectionMode
|
|
{
|
|
Faces,
|
|
Edges,
|
|
Vertices,
|
|
Loops,
|
|
Rings,
|
|
FacesEdgesVertices
|
|
};
|
|
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsSelectionModeToolBuilder : public UEditMeshPolygonsToolBuilder
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
EEditMeshPolygonsToolSelectionMode SelectionMode = EEditMeshPolygonsToolSelectionMode::Faces;
|
|
|
|
UE_API virtual bool CanBuildTool(const FToolBuilderState& SceneState) const override;
|
|
UE_API virtual void InitializeNewTool(USingleTargetWithSelectionTool* Tool, const FToolBuilderState& SceneState) const override;
|
|
};
|
|
|
|
|
|
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsToolActionPropertySet : public UInteractiveToolPropertySet
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
TWeakObjectPtr<UEditMeshPolygonsTool> ParentTool;
|
|
|
|
void Initialize(UEditMeshPolygonsTool* ParentToolIn) { ParentTool = ParentToolIn; }
|
|
|
|
UE_API void PostAction(EEditMeshPolygonsToolActions Action);
|
|
};
|
|
|
|
UCLASS(MinimalAPI)
|
|
class UPolyEditTopologyProperties : public UEditMeshPolygonsToolActionPropertySet
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
/**
|
|
* When true, adds extra corners at sharp group edge bends (in addition to the normal corners that
|
|
* are placed at junctures of three or more group edges). For instance, a single disconnected quad-like group
|
|
* would normally have a single group edge with no corners, since it has no neighboring groups, but this
|
|
* setting will allow for the generation of corners at the quad corners, which is very useful for editing.
|
|
* Note that the setting takes effect only after clicking Regenerate Extra Corners or performing some
|
|
* operation that changes the group topology.
|
|
*/
|
|
UPROPERTY(EditAnywhere, Category = TopologyOptions)
|
|
bool bAddExtraCorners = true;
|
|
|
|
UFUNCTION(CallInEditor, Category = TopologyOptions)
|
|
void RegenerateExtraCorners() { PostAction(EEditMeshPolygonsToolActions::RegenerateExtraCorners); }
|
|
|
|
/**
|
|
* When generating extra corners, how sharp the angle needs to be to warrant an extra corner placement there. Lower values require
|
|
* sharper corners, so are more tolerant of curved group edges. For instance, 180 will place corners at every vertex along a group
|
|
* edge even if the edge is perfectly straight, and 135 will place a vertex only once the edge bends 45 degrees off the straight path
|
|
* (i.e. 135 degrees to the previous edge).
|
|
* The setting is applied either when Regenerate Extra Corners is clicked, or after any operation that modifies topology.
|
|
*/
|
|
UPROPERTY(EditAnywhere, Category = TopologyOptions, meta = (ClampMin = "0", ClampMax = "180", EditCondition = "bAddExtraCorners"))
|
|
double ExtraCornerAngleThresholdDegrees = 135;
|
|
};
|
|
|
|
/** PolyEdit Actions */
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsToolActions : public UEditMeshPolygonsToolActionPropertySet
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
/** Extrude the current set of selected faces by moving and stitching them. */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Extrude", DisplayPriority = 1))
|
|
void Extrude() { PostAction(EEditMeshPolygonsToolActions::Extrude); }
|
|
|
|
/** Like Extrude/Offset, but performed in a boolean way, meaning that the faces can cut away the mesh or bridge mesh parts. */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Push/Pull", DisplayPriority = 1))
|
|
void PushPull() { PostAction(EEditMeshPolygonsToolActions::PushPull); }
|
|
|
|
/** Like Extrude, but defaults to moving verts along vertex normals instead of a single direction.*/
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Offset", DisplayPriority = 1))
|
|
void Offset() { PostAction(EEditMeshPolygonsToolActions::Offset); }
|
|
|
|
/**
|
|
* Inset the current set of selected faces. Click in viewport to confirm inset distance.
|
|
*
|
|
* (An Inset operation stitches in a smaller version of selected faces inside the existing ones)
|
|
*/
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Inset", DisplayPriority = 2))
|
|
void Inset() { PostAction(EEditMeshPolygonsToolActions::Inset); }
|
|
|
|
/**
|
|
* Outset the current set of selected faces. Click in viewport to confirm outset distance.
|
|
*
|
|
* (An Outset operation stitches in a larger version of selected faces inside the existing ones)
|
|
*/
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Outset", DisplayPriority = 3))
|
|
void Outset() { PostAction(EEditMeshPolygonsToolActions::Outset); }
|
|
|
|
/** Bevel the edge loops around the selected faces, inserting edge-aligned faces that interpolate the normals of the selected faces */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Bevel", DisplayPriority = 4))
|
|
void Bevel() { PostAction(EEditMeshPolygonsToolActions::BevelFaces); }
|
|
|
|
/** Merge the current set of selected faces into a single face */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Merge", DisplayPriority = 4))
|
|
void Merge() { PostAction(EEditMeshPolygonsToolActions::Merge); }
|
|
|
|
/** Delete the current set of selected faces */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Delete Faces", DisplayPriority = 4))
|
|
void Delete() { PostAction(EEditMeshPolygonsToolActions::Delete); }
|
|
|
|
/** Cut the current set of selected faces. Click twice in viewport to set cut line. */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "CutFaces", DisplayPriority = 5))
|
|
void CutFaces() { PostAction(EEditMeshPolygonsToolActions::CutFaces); }
|
|
|
|
/** Recalculate normals for the current set of selected faces */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "RecalcNormals", DisplayPriority = 6))
|
|
void RecalcNormals() { PostAction(EEditMeshPolygonsToolActions::RecalculateNormals); }
|
|
|
|
/** Flip normals and face orientation for the current set of selected faces */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Flip", DisplayPriority = 7))
|
|
void Flip() { PostAction(EEditMeshPolygonsToolActions::FlipNormals); }
|
|
|
|
/** Retriangulate each of the selected faces */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Retriangulate", DisplayPriority = 9))
|
|
void Retriangulate() { PostAction(EEditMeshPolygonsToolActions::Retriangulate); }
|
|
|
|
/** Split each of the selected faces into a separate polygon for each triangle */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Decompose", DisplayPriority = 10))
|
|
void Decompose() { PostAction(EEditMeshPolygonsToolActions::Decompose); }
|
|
|
|
/** Separate the selected faces at their borders */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Disconnect", DisplayPriority = 11))
|
|
void Disconnect() { PostAction(EEditMeshPolygonsToolActions::Disconnect); }
|
|
|
|
/** Duplicate the selected faces at their borders */
|
|
UFUNCTION(CallInEditor, Category = FaceEdits, meta = (DisplayName = "Duplicate", DisplayPriority = 12))
|
|
void Duplicate() { PostAction(EEditMeshPolygonsToolActions::Duplicate); }
|
|
|
|
/** Insert a chain of edges across quads (faces with four edges) in the mesh. Due to ambiguity, edges will not be inserted on non-quad faces. */
|
|
UFUNCTION(CallInEditor, Category = ShapeEdits, meta = (DisplayName = "InsertEdgeLoop", DisplayPriority = 13))
|
|
void InsertEdgeLoop() { PostAction(EEditMeshPolygonsToolActions::InsertEdgeLoop); }
|
|
|
|
/** Insert a new edge connecting existing edges or vertices on a single face */
|
|
UFUNCTION(CallInEditor, Category = ShapeEdits, meta = (DisplayName = "Insert Edge", DisplayPriority = 14))
|
|
void InsertEdge() { PostAction(EEditMeshPolygonsToolActions::InsertEdge); }
|
|
|
|
/** Simplify every polygon group by removing vertices on shared straight edges and retriangulating */
|
|
UFUNCTION(CallInEditor, Category = ShapeEdits, meta = (DisplayName = "SimplifyByGroups", DisplayPriority = 15))
|
|
void SimplifyByGroups() { PostAction(EEditMeshPolygonsToolActions::SimplifyByGroups); }
|
|
|
|
};
|
|
|
|
|
|
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsToolActions_Triangles : public UEditMeshPolygonsToolActionPropertySet
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
/** Extrude the current set of selected faces. Click in viewport to confirm extrude height. */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Extrude", DisplayPriority = 1))
|
|
void Extrude() { PostAction(EEditMeshPolygonsToolActions::Extrude); }
|
|
|
|
/** Like Extrude/Offset, but performed in a boolean way, meaning that the faces can cut away the mesh or bridge mesh parts. */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Push/Pull", DisplayPriority = 1))
|
|
void PushPull() { PostAction(EEditMeshPolygonsToolActions::PushPull); }
|
|
|
|
/** Like Extrude, but defaults to moving verts along vertex normals instead of a single direction. */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Offset", DisplayPriority = 1))
|
|
void Offset() { PostAction(EEditMeshPolygonsToolActions::Offset); }
|
|
|
|
/** Inset the current set of selected faces. Click in viewport to confirm inset distance. */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Inset", DisplayPriority = 2))
|
|
void Inset() { PostAction(EEditMeshPolygonsToolActions::Inset); }
|
|
|
|
/** Outset the current set of selected faces. Click in viewport to confirm outset distance. */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Outset", DisplayPriority = 3))
|
|
void Outset() { PostAction(EEditMeshPolygonsToolActions::Outset); }
|
|
|
|
/** Delete the current set of selected faces */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Delete Faces", DisplayPriority = 4))
|
|
void Delete() { PostAction(EEditMeshPolygonsToolActions::Delete); }
|
|
|
|
/** Cut the current set of selected faces. Click twice in viewport to set cut line. */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "CutFaces", DisplayPriority = 5))
|
|
void CutFaces() { PostAction(EEditMeshPolygonsToolActions::CutFaces); }
|
|
|
|
/** Recalculate normals for the current set of selected faces */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "RecalcNormals", DisplayPriority = 6))
|
|
void RecalcNormals() { PostAction(EEditMeshPolygonsToolActions::RecalculateNormals); }
|
|
|
|
/** Flip normals and face orientation for the current set of selected faces */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Flip", DisplayPriority = 7))
|
|
void Flip() { PostAction(EEditMeshPolygonsToolActions::FlipNormals); }
|
|
|
|
/** Separate the selected faces at their borders */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Disconnect", DisplayPriority = 11))
|
|
void Disconnect() { PostAction(EEditMeshPolygonsToolActions::Disconnect); }
|
|
|
|
/** Duplicate the selected faces */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Duplicate", DisplayPriority = 12))
|
|
void Duplicate() { PostAction(EEditMeshPolygonsToolActions::Duplicate); }
|
|
|
|
/** Insert a new vertex at the center of each selected face */
|
|
UFUNCTION(CallInEditor, Category = TriangleEdits, meta = (DisplayName = "Poke", DisplayPriority = 13))
|
|
void Poke() { PostAction(EEditMeshPolygonsToolActions::PokeSingleFace); }
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsToolUVActions : public UEditMeshPolygonsToolActionPropertySet
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
|
|
/** Assign planar-projection UVs to mesh */
|
|
UFUNCTION(CallInEditor, Category = UVs, meta = (DisplayName = "PlanarProjection", DisplayPriority = 11))
|
|
void PlanarProjection()
|
|
{
|
|
PostAction(EEditMeshPolygonsToolActions::PlanarProjectionUV);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsToolEdgeActions : public UEditMeshPolygonsToolActionPropertySet
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
/** Merge selected boundary edges, centering the result */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Weld", DisplayPriority = 0))
|
|
void WeldCentered() { PostAction(EEditMeshPolygonsToolActions::WeldEdgesCentered); }
|
|
|
|
/** Merge selected boundary edges, moving the first edge to the second */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Weld To", DisplayPriority = 1))
|
|
void Weld() { PostAction(EEditMeshPolygonsToolActions::WeldEdges); }
|
|
|
|
/** Make each selected polygroup edge follow a straight path between its endpoints */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Straighten", DisplayPriority = 2))
|
|
void Straighten() { PostAction(EEditMeshPolygonsToolActions::StraightenEdge); }
|
|
|
|
/** Fill the adjacent hole for any selected boundary edges */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Fill Hole", DisplayPriority = 3))
|
|
void FillHole() { PostAction(EEditMeshPolygonsToolActions::FillHole); }
|
|
|
|
/** Bevel the selected edges, replacing them with angled faces */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Bevel", DisplayPriority = 4))
|
|
void Bevel() { PostAction(EEditMeshPolygonsToolActions::BevelEdges); }
|
|
|
|
/** Create a new face that connects the selected edges */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Bridge", DisplayPriority = 5))
|
|
void Bridge() { PostAction(EEditMeshPolygonsToolActions::BridgeEdges); }
|
|
|
|
/** Duplicate and move boundary edge vertices outwards and connect them to the original boundary to create new faces. */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayPriority = 6))
|
|
void Extrude() { PostAction(EEditMeshPolygonsToolActions::ExtrudeEdges); }
|
|
|
|
/** Simplify the underlying triangulation along the selected edges, when doing so won't change the shape or UVs, or make low-quality triangles */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayPriority = 7))
|
|
void Simplify() { PostAction(EEditMeshPolygonsToolActions::SimplifyAlongEdges); }
|
|
|
|
/** Delete selected edge, implicitly merging any connected faces */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Delete Edges", DisplayPriority = 8))
|
|
void DeleteEdge() { PostAction(EEditMeshPolygonsToolActions::Delete); }
|
|
|
|
/** Collapse the selected edges, deleting the attached triangles and merging their vertices into one */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Collapse", DisplayPriority = 5))
|
|
void Collapse() { PostAction(EEditMeshPolygonsToolActions::CollapseEdge); }
|
|
};
|
|
|
|
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsToolEdgeActions_Triangles : public UEditMeshPolygonsToolActionPropertySet
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
/** Merge selected boundary edges, centering the result */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Weld", DisplayPriority = 0))
|
|
void WeldCentered() { PostAction(EEditMeshPolygonsToolActions::WeldEdgesCentered); }
|
|
|
|
/** Merge selected boundary edges, moving the first edge to the second */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Weld To", DisplayPriority = 1))
|
|
void Weld() { PostAction(EEditMeshPolygonsToolActions::WeldEdges); }
|
|
|
|
/** Fill the adjacent hole for any selected boundary edges */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Fill Hole", DisplayPriority = 2))
|
|
void FillHole() { PostAction(EEditMeshPolygonsToolActions::FillHole); }
|
|
|
|
/** Create a new face that connects the selected edges */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Bridge", DisplayPriority = 3))
|
|
void Bridge() { PostAction(EEditMeshPolygonsToolActions::BridgeEdges); }
|
|
|
|
/** Duplicate and move boundary vertices outwards and connect them to the original boundary to create new faces. */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Extrude", DisplayPriority = 4))
|
|
void Extrude() { PostAction(EEditMeshPolygonsToolActions::ExtrudeEdges); }
|
|
|
|
/** Collapse the selected edges, deleting the attached triangles and merging its two vertices into one */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Collapse", DisplayPriority = 5))
|
|
void Collapse() { PostAction(EEditMeshPolygonsToolActions::CollapseEdge); }
|
|
|
|
/** Flip the selected (non-border, non-seam) edges, replacing them with new edges in the crossing direction */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Flip", DisplayPriority = 6))
|
|
void Flip() { PostAction(EEditMeshPolygonsToolActions::FlipSingleEdge); }
|
|
|
|
/** Split the selected edges, inserting a new vertex at each edge midpoint */
|
|
UFUNCTION(CallInEditor, Category = EdgeEdits, meta = (DisplayName = "Split", DisplayPriority = 7))
|
|
void Split() { PostAction(EEditMeshPolygonsToolActions::SplitSingleEdge); }
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
UCLASS(MinimalAPI)
|
|
class UEditMeshPolygonsTool : public USingleTargetWithSelectionTool,
|
|
public IToolActivityHost,
|
|
public IMeshVertexCommandChangeTarget,
|
|
public IInteractiveToolNestedAcceptCancelAPI
|
|
{
|
|
GENERATED_BODY()
|
|
using FFrame3d = UE::Geometry::FFrame3d;
|
|
|
|
public:
|
|
UE_API UEditMeshPolygonsTool();
|
|
|
|
UE_API virtual void RegisterActions(FInteractiveToolActionSet& ActionSet) override;
|
|
UE_API void EnableTriangleMode();
|
|
|
|
// used by undo/redo
|
|
UE_API void RebuildTopologyWithGivenExtraCorners(const TSet<int32>& Vids);
|
|
|
|
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;
|
|
UE_API virtual void DrawHUD(FCanvas* Canvas, IToolsContextRenderAPI* RenderAPI) override;
|
|
|
|
virtual bool HasCancel() const override { return true; }
|
|
virtual bool HasAccept() const override { return true; }
|
|
|
|
UE_API virtual void OnPropertyModified(UObject* PropertySet, FProperty* Property) override;
|
|
|
|
// IInteractiveToolCameraFocusAPI implementation
|
|
UE_API virtual FBox GetWorldSpaceFocusBox() override;
|
|
UE_API virtual bool GetWorldSpaceFocusPoint(const FRay& WorldRay, FVector& PointOut) override;
|
|
|
|
// IToolActivityHost
|
|
UE_API virtual void NotifyActivitySelfEnded(UInteractiveToolActivity* Activity) override;
|
|
|
|
// IMeshVertexCommandChangeTarget
|
|
UE_API virtual void ApplyChange(const FMeshVertexChange* Change, bool bRevert) override;
|
|
|
|
// IInteractiveToolNestedAcceptCancelAPI
|
|
virtual bool SupportsNestedCancelCommand() override { return true; }
|
|
UE_API virtual bool CanCurrentlyNestedCancel() override;
|
|
UE_API virtual bool ExecuteNestedCancelCommand() override;
|
|
virtual bool SupportsNestedAcceptCommand() override { return true; }
|
|
UE_API virtual bool CanCurrentlyNestedAccept() override;
|
|
UE_API virtual bool ExecuteNestedAcceptCommand() override;
|
|
|
|
public:
|
|
|
|
UE_API virtual void RequestAction(EEditMeshPolygonsToolActions ActionType);
|
|
|
|
UE_API virtual void RequestSingleShotAction(EEditMeshPolygonsToolActions ActionType);
|
|
|
|
UE_API void SetActionButtonsVisibility(bool bVisible);
|
|
|
|
protected:
|
|
// If bTriangleMode = true, then we use a per-triangle FTriangleGroupTopology instead of polygroup topology.
|
|
// This allows low-level mesh editing with mainly the same code, at a significant cost in overhead.
|
|
// This is a fundamental mode switch, must be set before ::Setup() is called!
|
|
bool bTriangleMode;
|
|
|
|
// TODO: This is a hack to allow us to disallow any actions inside the tool after Setup() is called. We
|
|
// use it if the user tries to run the tool on a mesh that has too many edges for us to render, to avoid
|
|
// hanging the editor.
|
|
bool bToolDisabled = false;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UMeshOpPreviewWithBackgroundCompute> Preview = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditCommonProperties> CommonProps = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UEditMeshPolygonsToolActions> EditActions = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UEditMeshPolygonsToolActions_Triangles> EditActions_Triangles = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UEditMeshPolygonsToolEdgeActions> EditEdgeActions = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UEditMeshPolygonsToolEdgeActions_Triangles> EditEdgeActions_Triangles = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UEditMeshPolygonsToolUVActions> EditUVActions = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditTopologyProperties> TopologyProperties = nullptr;
|
|
|
|
/**
|
|
* Activity objects that handle multi-interaction operations
|
|
*/
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditExtrudeActivity> ExtrudeActivity = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditInsetOutsetActivity> InsetOutsetActivity = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditCutFacesActivity> CutFacesActivity = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditPlanarProjectionUVActivity> PlanarProjectionUVActivity = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditInsertEdgeActivity> InsertEdgeActivity = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditInsertEdgeLoopActivity> InsertEdgeLoopActivity = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditBevelEdgeActivity> BevelEdgeActivity = nullptr;
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditExtrudeEdgeActivity> ExtrudeEdgeActivity = nullptr;
|
|
|
|
TMap<UInteractiveToolActivity*, FText> ActivityLabels;
|
|
TMap<UInteractiveToolActivity*, FName> ActivityIconNames;
|
|
|
|
/**
|
|
* Points to one of the activities when it is active
|
|
*/
|
|
TObjectPtr<UInteractiveToolActivity> CurrentActivity = nullptr;
|
|
|
|
TSharedPtr<UE::Geometry::FDynamicMesh3> CurrentMesh;
|
|
TSharedPtr<UE::Geometry::FGroupTopology> Topology;
|
|
TSharedPtr<UE::Geometry::FDynamicMeshAABBTree3> MeshSpatial;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UPolyEditActivityContext> ActivityContext = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UPolygonSelectionMechanic> SelectionMechanic = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UDragAlignmentMechanic> DragAlignmentMechanic = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UCombinedTransformGizmo> TransformGizmo = nullptr;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<UTransformProxy> TransformProxy = nullptr;
|
|
|
|
FText DefaultMessage;
|
|
|
|
UE_API void ResetUserMessage();
|
|
|
|
bool bSelectionStateDirty = false;
|
|
UE_API void OnSelectionModifiedEvent();
|
|
|
|
UE_API void OnBeginGizmoTransform(UTransformProxy* Proxy);
|
|
UE_API void OnEndGizmoTransform(UTransformProxy* Proxy);
|
|
UE_API void OnGizmoTransformChanged(UTransformProxy* Proxy, FTransform Transform);
|
|
UE_API void UpdateGizmoFrame(const FFrame3d* UseFrame = nullptr);
|
|
FFrame3d LastGeometryFrame;
|
|
FFrame3d LastTransformerFrame;
|
|
FFrame3d LockedTransfomerFrame;
|
|
bool bInGizmoDrag = false;
|
|
|
|
UE::Geometry::FTransformSRT3d BakedTransform; // We bake the scale part of the Target -> World transform
|
|
UE::Geometry::FTransformSRT3d WorldTransform; // Transform from Baked to World
|
|
|
|
FFrame3d InitialGizmoFrame;
|
|
FVector3d InitialGizmoScale;
|
|
bool bGizmoUpdatePending = false;
|
|
FFrame3d LastUpdateGizmoFrame;
|
|
FVector3d LastUpdateGizmoScale;
|
|
bool bLastUpdateUsedWorldFrame = false;
|
|
UE_API void ComputeUpdate_Gizmo();
|
|
|
|
UE_API UE::Geometry::FDynamicMeshAABBTree3& GetSpatial();
|
|
bool bSpatialDirty;
|
|
|
|
// UV Scale factor to apply to texturing on any new geometry (e.g. new faces added by extrude)
|
|
float UVScaleFactor = 1.0f;
|
|
|
|
EEditMeshPolygonsToolActions PendingAction = EEditMeshPolygonsToolActions::NoAction;
|
|
bool bTerminateOnPendingActionComplete = false;
|
|
|
|
int32 ActivityTimestamp = 1;
|
|
|
|
UE_API void StartActivity(TObjectPtr<UInteractiveToolActivity> Activity);
|
|
UE_API void EndCurrentActivity(EToolShutdownType ShutdownType);
|
|
UE_API void SetActionButtonPanelsVisible(bool bVisible);
|
|
|
|
// Emit an undoable change to CurrentMesh and update related structures (preview, spatial, etc)
|
|
UE_API void EmitCurrentMeshChangeAndUpdate(const FText& TransactionLabel,
|
|
TUniquePtr<UE::Geometry::FDynamicMeshChange> MeshChangeIn,
|
|
const UE::Geometry::FGroupTopologySelection& OutputSelection);
|
|
|
|
// Emit an undoable start of an activity
|
|
UE_API void EmitActivityStart(const FText& TransactionLabel);
|
|
|
|
UE_API void UpdateGizmoVisibility();
|
|
|
|
UE_API void ApplyDelete();
|
|
|
|
UE_API void ApplyMerge();
|
|
UE_API void ApplyDeleteFaces();
|
|
UE_API void ApplyRecalcNormals();
|
|
UE_API void ApplyFlipNormals();
|
|
UE_API void ApplyRetriangulate();
|
|
UE_API void ApplyDecompose();
|
|
UE_API void ApplyDisconnect();
|
|
UE_API void ApplyDuplicate();
|
|
UE_API void ApplyPokeSingleFace();
|
|
|
|
UE_API void ApplyCollapseEdge();
|
|
UE_API void ApplyWeldEdges();
|
|
UE_API void ApplyStraightenEdges();
|
|
UE_API void ApplyDeleteEdges();
|
|
UE_API void ApplyFillHole();
|
|
UE_API void ApplyBridgeEdges();
|
|
UE_API void ApplySimplifyAlongEdges();
|
|
|
|
UE_API void ApplyFlipSingleEdge();
|
|
UE_DEPRECATED(5.5, "Use ApplyCollapseEdge instead.")
|
|
UE_API void ApplyCollapseSingleEdge();
|
|
UE_API void ApplySplitSingleEdge();
|
|
|
|
UE_API void SimplifyByGroups();
|
|
|
|
UE_API void ApplyRegenerateExtraCorners();
|
|
double ExtraCornerDotProductThreshold = -1;
|
|
|
|
FFrame3d ActiveSelectionFrameLocal;
|
|
FFrame3d ActiveSelectionFrameWorld;
|
|
TArray<int32> ActiveTriangleSelection;
|
|
UE::Geometry::FAxisAlignedBox3d ActiveSelectionBounds;
|
|
|
|
struct FSelectedEdge
|
|
{
|
|
int32 EdgeTopoID;
|
|
TArray<int32> EdgeIDs;
|
|
};
|
|
TArray<FSelectedEdge> ActiveEdgeSelection;
|
|
|
|
enum class EPreviewMaterialType
|
|
{
|
|
SourceMaterials, PreviewMaterial, UVMaterial
|
|
};
|
|
UE_API void UpdateEditPreviewMaterials(EPreviewMaterialType MaterialType);
|
|
EPreviewMaterialType CurrentPreviewMaterial;
|
|
|
|
|
|
//
|
|
// data for current drag
|
|
//
|
|
UE::Geometry::FGroupTopologyDeformer LinearDeformer;
|
|
UE_API void UpdateDeformerFromSelection(const UE::Geometry::FGroupTopologySelection& Selection);
|
|
|
|
FMeshVertexChangeBuilder* ActiveVertexChange;
|
|
UE_API void UpdateDeformerChangeFromROI(bool bFinal);
|
|
UE_API void BeginDeformerChange();
|
|
UE_API void EndDeformerChange();
|
|
|
|
UE_API bool BeginMeshFaceEditChange();
|
|
|
|
UE_API bool BeginMeshEdgeEditChange();
|
|
UE_API bool BeginMeshBoundaryEdgeEditChange(bool bOnlySimple);
|
|
UE_API bool BeginMeshEdgeEditChange(TFunctionRef<bool(int32)> GroupEdgeIDFilterFunc);
|
|
|
|
UE_API void UpdateFromCurrentMesh(bool bRebuildTopology);
|
|
int32 ModifiedTopologyCounter = 0;
|
|
bool bWasTopologyEdited = false;
|
|
|
|
friend class FEditMeshPolygonsToolMeshChange;
|
|
friend class FPolyEditActivityStartChange;
|
|
|
|
// custom setup support
|
|
friend class UEditMeshPolygonsSelectionModeToolBuilder;
|
|
friend class UEditMeshPolygonsActionModeToolBuilder;
|
|
TUniqueFunction<void(UEditMeshPolygonsTool*)> PostSetupFunction;
|
|
UE_API void SetToSelectionModeInterface();
|
|
|
|
private:
|
|
UE_API void ApplyWeldEdges(double InterpolationT);
|
|
UE_API void ApplyWeldVertices(double InterpolationT);
|
|
UE_API void CollapseGroupEdges(TSet<int32>& GroupEdgesToCollapse,
|
|
UE::Geometry::FDynamicMeshChangeTracker& ChangeTracker);
|
|
};
|
|
|
|
|
|
/**
|
|
* Wraps a FDynamicMeshChange so that it can be expired and so that other data
|
|
* structures in the tool can be updated. On apply/revert, the topology is rebuilt
|
|
* using stored extra corner vids.
|
|
*/
|
|
class FEditMeshPolygonsToolMeshChange : public FToolCommandChange
|
|
{
|
|
public:
|
|
FEditMeshPolygonsToolMeshChange(TUniquePtr<UE::Geometry::FDynamicMeshChange> MeshChangeIn)
|
|
: MeshChange(MoveTemp(MeshChangeIn))
|
|
{};
|
|
|
|
UE_API virtual void Apply(UObject* Object) override;
|
|
UE_API virtual void Revert(UObject* Object) override;
|
|
UE_API virtual FString ToString() const override;
|
|
|
|
TSet<int32> ExtraCornerVidsBefore;
|
|
TSet<int32> ExtraCornerVidsAfter;
|
|
protected:
|
|
TUniquePtr<UE::Geometry::FDynamicMeshChange> MeshChange;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* FPolyEditActivityStartChange is used to cancel out of an active action on Undo.
|
|
* No action is taken on Redo, ie we do not re-start the Tool on Redo.
|
|
*/
|
|
class FPolyEditActivityStartChange : public FToolCommandChange
|
|
{
|
|
public:
|
|
FPolyEditActivityStartChange(int32 ActivityTimestampIn)
|
|
{
|
|
ActivityTimestamp = ActivityTimestampIn;
|
|
}
|
|
virtual void Apply(UObject* Object) override {}
|
|
UE_API virtual void Revert(UObject* Object) override;
|
|
UE_API virtual bool HasExpired(UObject* Object) const override;
|
|
UE_API virtual FString ToString() const override;
|
|
|
|
protected:
|
|
bool bHaveDoneUndo = false;
|
|
int32 ActivityTimestamp = 0;
|
|
};
|
|
|
|
#undef UE_API
|