2020-08-11 01:36:57 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "RevolveBoundaryTool.h"
2021-06-02 15:58:00 -04:00
# include "ModelingObjectsCreationAPI.h"
2020-08-11 01:36:57 -04:00
# include "BaseBehaviors/SingleClickBehavior.h"
# include "CoreMinimal.h"
# include "CompositionOps/CurveSweepOp.h"
# include "InteractiveToolManager.h"
# include "Mechanics/ConstructionPlaneMechanic.h"
# include "Selection/PolygonSelectionMechanic.h"
# include "GroupTopology.h"
# include "ToolBuilderUtil.h"
# include "Selection/ToolSelectionUtil.h"
# include "ToolSceneQueriesUtil.h"
# include "ToolSetupUtil.h"
2021-03-24 11:11:02 -04:00
# include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
2021-03-09 19:33:56 -04:00
# include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types)
using namespace UE : : Geometry ;
2020-08-11 01:36:57 -04:00
# define LOCTEXT_NAMESPACE "URevolveBoundaryTool"
// Tool builder
2021-03-24 11:11:02 -04:00
USingleSelectionMeshEditingTool * URevolveBoundaryToolBuilder : : CreateNewTool ( const FToolBuilderState & SceneState ) const
2020-08-11 01:36:57 -04:00
{
2021-03-24 11:11:02 -04:00
return NewObject < URevolveBoundaryTool > ( SceneState . ToolManager ) ;
2020-08-11 01:36:57 -04:00
}
// Operator factory
TUniquePtr < FDynamicMeshOperator > URevolveBoundaryOperatorFactory : : MakeNewOperator ( )
{
TUniquePtr < FCurveSweepOp > CurveSweepOp = MakeUnique < FCurveSweepOp > ( ) ;
// Assemble profile curve
const FGroupTopologySelection & ActiveSelection = RevolveBoundaryTool - > SelectionMechanic - > GetActiveSelection ( ) ;
if ( ActiveSelection . SelectedEdgeIDs . Num ( ) = = 1 )
{
2020-09-24 00:43:27 -04:00
int32 EdgeID = ActiveSelection . GetASelectedEdgeID ( ) ;
2020-08-11 01:36:57 -04:00
if ( RevolveBoundaryTool - > Topology - > IsBoundaryEdge ( EdgeID ) )
{
const TArray < int32 > & VertexIndices = RevolveBoundaryTool - > Topology - > GetGroupEdgeVertices ( EdgeID ) ;
2021-03-24 11:11:02 -04:00
FTransform ToWorld = Cast < IPrimitiveComponentBackedTarget > ( RevolveBoundaryTool - > Target ) - > GetWorldTransform ( ) ;
2020-08-11 01:36:57 -04:00
// Boundary loop includes the last vertex twice, so stop early.
CurveSweepOp - > ProfileCurve . Reserve ( VertexIndices . Num ( ) - 1 ) ;
for ( int32 i = 0 ; i < VertexIndices . Num ( ) - 1 ; + + i )
{
int32 VertIndex = VertexIndices [ i ] ;
2021-03-30 21:25:22 -04:00
FVector3d NewPos = ( FVector3d ) ToWorld . TransformPosition ( ( FVector ) RevolveBoundaryTool - > OriginalMesh - > GetVertex ( VertIndex ) ) ;
CurveSweepOp - > ProfileCurve . Add ( NewPos ) ;
2020-08-11 01:36:57 -04:00
}
CurveSweepOp - > bProfileCurveIsClosed = true ;
}
}
RevolveBoundaryTool - > Settings - > ApplyToCurveSweepOp ( * RevolveBoundaryTool - > MaterialProperties ,
RevolveBoundaryTool - > RevolutionAxisOrigin , RevolveBoundaryTool - > RevolutionAxisDirection , * CurveSweepOp ) ;
return CurveSweepOp ;
}
// Tool itself
void URevolveBoundaryTool : : Setup ( )
{
UMeshBoundaryToolBase : : Setup ( ) ;
2020-11-13 12:09:55 -04:00
// We're actually going to handle the selection clicks ourselves so that we can align axis if
// we want to.
SelectionMechanic - > DisableBehaviors ( this ) ;
SelectionMechanic - > SetShouldAddToSelectionFunc ( [ ] ( ) { return false ; } ) ;
SelectionMechanic - > SetShouldRemoveFromSelectionFunc ( [ ] ( ) { return false ; } ) ;
USingleClickInputBehavior * ClickBehavior = NewObject < USingleClickInputBehavior > ( ) ;
ClickBehavior - > Initialize ( this ) ;
ClickBehavior - > Modifiers . RegisterModifier ( AlignAxisModifier , FInputDeviceState : : IsCtrlKeyDown ) ;
AddInputBehavior ( ClickBehavior ) ;
2020-08-11 01:36:57 -04:00
Settings = NewObject < URevolveBoundaryToolProperties > ( this ) ;
Settings - > RestoreProperties ( this ) ;
AddToolPropertySource ( Settings ) ;
MaterialProperties = NewObject < UNewMeshMaterialProperties > ( this ) ;
AddToolPropertySource ( MaterialProperties ) ;
MaterialProperties - > RestoreProperties ( this ) ;
2020-10-09 22:42:26 -04:00
UpdateRevolutionAxis ( ) ;
2020-08-11 01:36:57 -04:00
// The plane mechanic is used for the revolution axis
PlaneMechanic = NewObject < UConstructionPlaneMechanic > ( this ) ;
PlaneMechanic - > Setup ( this ) ;
2020-10-09 22:42:26 -04:00
PlaneMechanic - > Initialize ( TargetWorld , FFrame3d ( Settings - > AxisOrigin ,
FRotator ( Settings - > AxisPitch , Settings - > AxisYaw , 0 ) . Quaternion ( ) ) ) ;
2020-11-13 12:09:55 -04:00
PlaneMechanic - > UpdateClickPriority ( ClickBehavior - > GetPriority ( ) . MakeLower ( ) ) ;
2020-08-11 01:36:57 -04:00
PlaneMechanic - > bShowGrid = false ;
PlaneMechanic - > OnPlaneChanged . AddLambda ( [ this ] ( ) {
2020-10-09 22:42:26 -04:00
Settings - > AxisOrigin = ( FVector ) PlaneMechanic - > Plane . Origin ;
FRotator AxisOrientation = ( ( FQuat ) PlaneMechanic - > Plane . Rotation ) . Rotator ( ) ;
Settings - > AxisPitch = AxisOrientation . Pitch ;
Settings - > AxisYaw = AxisOrientation . Yaw ;
UpdateRevolutionAxis ( ) ;
2020-08-11 01:36:57 -04:00
} ) ;
PlaneMechanic - > SetEnableGridSnaping ( Settings - > bSnapToWorldGrid ) ;
2021-03-24 11:11:02 -04:00
Cast < IPrimitiveComponentBackedTarget > ( Target ) - > SetOwnerVisibility ( Settings - > bDisplayOriginalMesh ) ;
2020-08-11 01:36:57 -04:00
2021-02-08 17:02:09 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Revolve Boundary " ) ) ;
2020-08-11 01:36:57 -04:00
GetToolManager ( ) - > DisplayMessage (
2021-02-08 17:02:09 -04:00
LOCTEXT ( " OnStartRevolveBoundaryTool " , " Revolve an open mesh boundary loop around an axis to create a new mesh. Ctrl+click will reposition the revolution axis, potentially aligning it with an edge. " ) ,
2020-08-11 01:36:57 -04:00
EToolMessageLevel : : UserNotification ) ;
if ( Topology - > Edges . Num ( ) = = 1 )
{
FGroupTopologySelection Selection ;
Selection . SelectedEdgeIDs . Add ( 0 ) ;
SelectionMechanic - > SetSelection ( Selection ) ;
StartPreview ( ) ;
}
else if ( Topology - > Edges . Num ( ) = = 0 )
{
GetToolManager ( ) - > DisplayMessage (
2020-10-22 19:19:16 -04:00
LOCTEXT ( " NoBoundaryLoops " , " This mesh does not have any boundary loops to display and revolve. Delete some faces or use a different mesh. " ) ,
2020-08-11 01:36:57 -04:00
EToolMessageLevel : : UserWarning ) ;
}
else
{
GetToolManager ( ) - > DisplayMessage (
2020-10-22 19:19:16 -04:00
LOCTEXT ( " OnStartRevolveBoundaryToolMultipleBoundaries " , " Your mesh has multiple boundaries- Click the one you wish to use " ) ,
2020-08-11 01:36:57 -04:00
EToolMessageLevel : : UserWarning ) ;
}
}
void URevolveBoundaryTool : : OnUpdateModifierState ( int ModifierId , bool bIsOn )
{
if ( ModifierId = = AlignAxisModifier )
{
bAlignAxisOnClick = bIsOn ;
}
}
2020-11-13 12:09:55 -04:00
FInputRayHit URevolveBoundaryTool : : IsHitByClick ( const FInputDeviceRay & ClickPos )
{
FHitResult OutHit ;
if ( SelectionMechanic - > TopologyHitTest ( ClickPos . WorldRay , OutHit ) )
{
return FInputRayHit ( OutHit . Distance ) ;
}
return FInputRayHit ( ) ; // bHit is false
}
2020-08-11 01:36:57 -04:00
void URevolveBoundaryTool : : OnClicked ( const FInputDeviceRay & ClickPos )
{
// Update selection only if we clicked on something. We don't want to be able to
// clear a selection with a click.
FHitResult HitResult ;
if ( SelectionMechanic - > TopologyHitTest ( ClickPos . WorldRay , HitResult ) )
{
FVector3d LocalHitPosition , LocalHitNormal ;
SelectionMechanic - > UpdateSelection ( ClickPos . WorldRay , LocalHitPosition , LocalHitNormal ) ;
// Clear the "multiple boundaries" warning, since we've selected one.
GetToolManager ( ) - > DisplayMessage ( FText ( ) , EToolMessageLevel : : UserWarning ) ;
// If Ctrl is pressed, we also want to align the revolution axis to the edge that we clicked
if ( bAlignAxisOnClick )
{
const FGroupTopologySelection & Selection = SelectionMechanic - > GetActiveSelection ( ) ;
2020-09-24 00:43:27 -04:00
int32 ClickedEid = Topology - > GetGroupEdgeEdges ( Selection . GetASelectedEdgeID ( ) ) [ HitResult . Item ] ;
2020-08-11 01:36:57 -04:00
FVector3d VertexA , VertexB ;
OriginalMesh - > GetEdgeV ( ClickedEid , VertexA , VertexB ) ;
2021-03-24 11:11:02 -04:00
FTransform ToWorldTranform = Cast < IPrimitiveComponentBackedTarget > ( Target ) - > GetWorldTransform ( ) ;
2021-03-30 21:25:22 -04:00
FLine3d EdgeLine = FLine3d : : FromPoints ( ( FVector3d ) ToWorldTranform . TransformPosition ( ( FVector ) VertexA ) ,
( FVector3d ) ToWorldTranform . TransformPosition ( ( FVector ) VertexB ) ) ;
2020-08-11 01:36:57 -04:00
2020-10-09 22:42:26 -04:00
FFrame3d RevolutionAxisFrame ;
2021-03-30 21:25:22 -04:00
RevolutionAxisFrame . Origin = EdgeLine . NearestPoint ( ( FVector3d ) HitResult . ImpactPoint ) ;
2020-10-09 22:42:26 -04:00
RevolutionAxisFrame . AlignAxis ( 0 , EdgeLine . Direction ) ;
2020-08-11 01:36:57 -04:00
2020-10-09 22:42:26 -04:00
PlaneMechanic - > SetPlaneWithoutBroadcast ( RevolutionAxisFrame ) ;
Settings - > AxisOrigin = ( FVector ) RevolutionAxisFrame . Origin ;
FRotator AxisOrientation = ( ( FQuat ) RevolutionAxisFrame . Rotation ) . Rotator ( ) ;
Settings - > AxisPitch = AxisOrientation . Pitch ;
Settings - > AxisYaw = AxisOrientation . Yaw ;
UpdateRevolutionAxis ( ) ;
2020-08-11 01:36:57 -04:00
}
// Update the preview
if ( Preview = = nullptr )
{
StartPreview ( ) ;
}
else
{
Preview - > InvalidateResult ( ) ;
}
}
}
bool URevolveBoundaryTool : : CanAccept ( ) const
{
2021-02-05 16:33:02 -04:00
return Preview ! = nullptr & & Preview - > HaveValidNonEmptyResult ( ) ;
2020-08-11 01:36:57 -04:00
}
2020-10-09 22:42:26 -04:00
/**
* Uses the settings stored in the properties object to update the revolution axis
*/
void URevolveBoundaryTool : : UpdateRevolutionAxis ( )
2020-08-11 01:36:57 -04:00
{
2021-03-30 21:25:22 -04:00
RevolutionAxisOrigin = ( FVector3d ) Settings - > AxisOrigin ;
RevolutionAxisDirection = ( FVector3d ) FRotator ( Settings - > AxisPitch , Settings - > AxisYaw , 0 ) . RotateVector ( FVector ( 1 , 0 , 0 ) ) ;
2020-08-11 01:36:57 -04:00
if ( Preview )
{
Preview - > InvalidateResult ( ) ;
}
}
void URevolveBoundaryTool : : StartPreview ( )
{
URevolveBoundaryOperatorFactory * RevolveBoundaryOpCreator = NewObject < URevolveBoundaryOperatorFactory > ( ) ;
RevolveBoundaryOpCreator - > RevolveBoundaryTool = this ;
Preview = NewObject < UMeshOpPreviewWithBackgroundCompute > ( RevolveBoundaryOpCreator ) ;
Preview - > Setup ( TargetWorld , RevolveBoundaryOpCreator ) ;
2021-06-11 22:39:18 -04:00
Preview - > PreviewMesh - > SetTangentsMode ( EDynamicMeshComponentTangentsMode : : AutoCalculated ) ;
2020-08-11 01:36:57 -04:00
2020-10-09 22:42:26 -04:00
Preview - > ConfigureMaterials ( MaterialProperties - > Material . Get ( ) ,
2020-08-11 01:36:57 -04:00
ToolSetupUtil : : GetDefaultWorkingMaterial ( GetToolManager ( ) ) ) ;
Preview - > PreviewMesh - > EnableWireframe ( MaterialProperties - > bWireframe ) ;
2021-02-05 16:33:02 -04:00
Preview - > OnMeshUpdated . AddLambda (
[ this ] ( const UMeshOpPreviewWithBackgroundCompute * UpdatedPreview )
{
UpdateAcceptWarnings ( UpdatedPreview - > HaveEmptyResult ( ) ? EAcceptWarning : : EmptyForbidden : EAcceptWarning : : NoWarning ) ;
}
) ;
2020-08-11 01:36:57 -04:00
Preview - > SetVisibility ( true ) ;
Preview - > InvalidateResult ( ) ;
}
void URevolveBoundaryTool : : Shutdown ( EToolShutdownType ShutdownType )
{
UMeshBoundaryToolBase : : Shutdown ( ShutdownType ) ;
Settings - > SaveProperties ( this ) ;
MaterialProperties - > SaveProperties ( this ) ;
PlaneMechanic - > Shutdown ( ) ;
2021-03-24 11:11:02 -04:00
Cast < IPrimitiveComponentBackedTarget > ( Target ) - > SetOwnerVisibility ( true ) ;
2020-08-11 01:36:57 -04:00
if ( Preview )
{
if ( ShutdownType = = EToolShutdownType : : Accept )
{
GenerateAsset ( Preview - > Shutdown ( ) ) ;
}
else
{
Preview - > Cancel ( ) ;
}
}
}
2021-06-02 15:58:00 -04:00
void URevolveBoundaryTool : : GenerateAsset ( const FDynamicMeshOpResult & OpResult )
2020-08-11 01:36:57 -04:00
{
2021-06-02 15:58:00 -04:00
if ( OpResult . Mesh - > TriangleCount ( ) < = 0 )
2021-01-19 14:14:05 -04:00
{
return ;
}
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " RevolveBoundaryToolTransactionName " , " Revolve Tool " ) ) ;
2020-08-11 01:36:57 -04:00
2021-06-02 15:58:00 -04:00
FCreateMeshObjectParams NewMeshObjectParams ;
NewMeshObjectParams . TargetWorld = TargetWorld ;
NewMeshObjectParams . Transform = ( FTransform ) OpResult . Transform ;
NewMeshObjectParams . BaseName = TEXT ( " Revolve " ) ;
NewMeshObjectParams . Materials . Add ( MaterialProperties - > Material . Get ( ) ) ;
NewMeshObjectParams . SetMesh ( OpResult . Mesh . Get ( ) ) ;
FCreateMeshObjectResult Result = UE : : Modeling : : CreateMeshObject ( GetToolManager ( ) , MoveTemp ( NewMeshObjectParams ) ) ;
if ( Result . IsOK ( ) & & Result . NewActor ! = nullptr )
2020-08-11 01:36:57 -04:00
{
2021-06-02 15:58:00 -04:00
ToolSelectionUtil : : SetNewActorSelection ( GetToolManager ( ) , Result . NewActor ) ;
2020-08-11 01:36:57 -04:00
}
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
void URevolveBoundaryTool : : OnTick ( float DeltaTime )
{
if ( PlaneMechanic ! = nullptr )
{
PlaneMechanic - > Tick ( DeltaTime ) ;
}
if ( Preview )
{
Preview - > Tick ( DeltaTime ) ;
}
}
void URevolveBoundaryTool : : Render ( IToolsContextRenderAPI * RenderAPI )
{
UMeshBoundaryToolBase : : Render ( RenderAPI ) ;
FViewCameraState CameraState ;
GetToolManager ( ) - > GetContextQueriesAPI ( ) - > GetCurrentViewState ( CameraState ) ;
if ( PlaneMechanic ! = nullptr )
{
PlaneMechanic - > Render ( RenderAPI ) ;
// Draw the axis of rotation
float PdiScale = CameraState . GetPDIScalingFactor ( ) ;
FPrimitiveDrawInterface * PDI = RenderAPI - > GetPrimitiveDrawInterface ( ) ;
FColor AxisColor ( 240 , 16 , 240 ) ;
double AxisThickness = 1 * PdiScale ;
double AxisHalfLength = ToolSceneQueriesUtil : : CalculateDimensionFromVisualAngleD ( CameraState , RevolutionAxisOrigin , 90 ) ;
FVector3d StartPoint = RevolutionAxisOrigin - ( RevolutionAxisDirection * ( AxisHalfLength * PdiScale ) ) ;
FVector3d EndPoint = RevolutionAxisOrigin + ( RevolutionAxisDirection * ( AxisHalfLength * PdiScale ) ) ;
PDI - > DrawLine ( ( FVector ) StartPoint , ( FVector ) EndPoint , AxisColor , SDPG_Foreground ,
AxisThickness , 0.0f , true ) ;
}
}
void URevolveBoundaryTool : : OnPropertyModified ( UObject * PropertySet , FProperty * Property )
{
2020-10-09 22:42:26 -04:00
PlaneMechanic - > SetPlaneWithoutBroadcast ( FFrame3d ( Settings - > AxisOrigin ,
FRotator ( Settings - > AxisPitch , Settings - > AxisYaw , 0 ) . Quaternion ( ) ) ) ;
UpdateRevolutionAxis ( ) ;
2020-08-11 01:36:57 -04:00
2021-03-24 11:11:02 -04:00
Cast < IPrimitiveComponentBackedTarget > ( Target ) - > SetOwnerVisibility ( Settings - > bDisplayOriginalMesh ) ;
2020-08-11 01:36:57 -04:00
PlaneMechanic - > SetEnableGridSnaping ( Settings - > bSnapToWorldGrid ) ;
if ( Preview )
{
if ( Property & & ( Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UNewMeshMaterialProperties , Material ) ) )
{
2020-10-09 22:42:26 -04:00
Preview - > ConfigureMaterials ( MaterialProperties - > Material . Get ( ) ,
2020-08-11 01:36:57 -04:00
ToolSetupUtil : : GetDefaultWorkingMaterial ( GetToolManager ( ) ) ) ;
}
Preview - > PreviewMesh - > EnableWireframe ( MaterialProperties - > bWireframe ) ;
Preview - > InvalidateResult ( ) ;
}
}
2020-10-09 22:42:26 -04:00
# undef LOCTEXT_NAMESPACE