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"
2022-03-17 14:03:58 -04:00
# include "SceneManagement.h" // FPrimitiveDrawInterface
2020-08-11 01:36:57 -04:00
# 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
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 ) ;
2022-01-06 16:44:09 +00:00
ClickBehavior - > Modifiers . RegisterModifier ( CtrlModifier , FInputDeviceState : : IsCtrlKeyDown ) ;
ClickBehavior - > Modifiers . RegisterModifier ( ShiftModifier , FInputDeviceState : : IsShiftKeyDown ) ;
2020-11-13 12:09:55 -04:00
AddInputBehavior ( ClickBehavior ) ;
2021-11-18 14:37:34 -05:00
OutputTypeProperties = NewObject < UCreateMeshObjectTypeProperties > ( this ) ;
OutputTypeProperties - > RestoreProperties ( this ) ;
OutputTypeProperties - > InitializeDefault ( ) ;
OutputTypeProperties - > WatchProperty ( OutputTypeProperties - > OutputType , [ this ] ( FString ) { OutputTypeProperties - > UpdatePropertyVisibility ( ) ; } ) ;
AddToolPropertySource ( OutputTypeProperties ) ;
2020-11-13 12:09:55 -04:00
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 ) ;
2022-01-28 10:18:10 -05:00
PlaneMechanic - > Initialize ( GetTargetWorld ( ) , FFrame3d ( Settings - > AxisOrigin ,
2021-10-26 13:33:36 -04:00
FRotator ( Settings - > AxisOrientation . X , Settings - > AxisOrientation . Y , 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 ( ) ;
2021-10-26 13:33:36 -04:00
Settings - > AxisOrientation . X = AxisOrientation . Pitch ;
Settings - > AxisOrientation . Y = AxisOrientation . Yaw ;
2022-01-06 16:44:09 +00:00
NotifyOfPropertyChangeByTool ( Settings ) ;
2020-10-09 22:42:26 -04:00
UpdateRevolutionAxis ( ) ;
2020-08-11 01:36:57 -04:00
} ) ;
2021-10-26 13:33:36 -04:00
Cast < IPrimitiveComponentBackedTarget > ( Target ) - > SetOwnerVisibility ( Settings - > bDisplayInputMesh ) ;
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 (
2022-01-06 16:44:09 +00:00
LOCTEXT ( " OnStartRevolveBoundaryTool " , " Revolve an open mesh boundary loop around an axis to create a new mesh. Ctrl+Click to align the axis to a surface/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 (
2021-10-26 13:33:36 -04:00
LOCTEXT ( " NoBoundaryLoops " , " This mesh does not have any boundary loops to display and revolve. Delete faces to create a boundary or use a different mesh. " ) ,
2020-08-11 01:36:57 -04:00
EToolMessageLevel : : UserWarning ) ;
}
else
{
GetToolManager ( ) - > DisplayMessage (
2021-10-26 13:33:36 -04:00
LOCTEXT ( " OnStartRevolveBoundaryToolMultipleBoundaries " , " Your mesh has multiple boundaries. Select the one you wish to use. " ) ,
2020-08-11 01:36:57 -04:00
EToolMessageLevel : : UserWarning ) ;
}
}
void URevolveBoundaryTool : : OnUpdateModifierState ( int ModifierId , bool bIsOn )
{
2022-01-06 16:44:09 +00:00
if ( ModifierId = = CtrlModifier )
2020-08-11 01:36:57 -04:00
{
2022-01-06 16:44:09 +00:00
// Like with the plane mechanic, clicking an edge while holding ctrl should move the axis
// to that point and (by default) align it.
bMoveAxisOnClick = bIsOn ;
}
else if ( ModifierId = = ShiftModifier )
{
// Like with the plane mechanic, holding ctrl + shift shifts the axis to the point without
// changing the orientation
bAlignAxisOnClick = ! bIsOn ;
2020-08-11 01:36:57 -04:00
}
}
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 ) ;
2022-01-06 16:44:09 +00:00
// Act on Ctrl/Shift modifiers the same way that the plane mechanic does.
if ( bMoveAxisOnClick )
2020-08-11 01:36:57 -04:00
{
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
2022-01-06 16:44:09 +00:00
// New frame starts as the old one with modified origin
FFrame3d RevolutionAxisFrame ( EdgeLine . NearestPoint ( ( FVector3d ) HitResult . ImpactPoint ) , PlaneMechanic - > Plane . Rotation ) ;
if ( bAlignAxisOnClick )
{
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 ( ) ;
2021-10-26 13:33:36 -04:00
Settings - > AxisOrientation . X = AxisOrientation . Pitch ;
Settings - > AxisOrientation . Y = AxisOrientation . Yaw ;
2020-10-09 22:42:26 -04:00
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 ;
2021-10-26 13:33:36 -04:00
RevolutionAxisDirection = ( FVector3d ) FRotator ( Settings - > AxisOrientation . X , Settings - > AxisOrientation . Y , 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 ) ;
2022-01-28 10:18:10 -05:00
Preview - > Setup ( GetTargetWorld ( ) , RevolveBoundaryOpCreator ) ;
2021-10-07 22:25:54 -04:00
ToolSetupUtil : : ApplyRenderingConfigurationToPreview ( Preview - > PreviewMesh , nullptr ) ;
2021-06-11 22:42:32 -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 ( ) ) ) ;
2021-10-18 14:21:53 -04:00
Preview - > PreviewMesh - > EnableWireframe ( MaterialProperties - > bShowWireframe ) ;
2020-08-11 01:36:57 -04:00
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 ( ) ;
}
2022-01-28 18:40:54 -05:00
void URevolveBoundaryTool : : OnShutdown ( EToolShutdownType ShutdownType )
2020-08-11 01:36:57 -04:00
{
2022-01-28 18:40:54 -05:00
UMeshBoundaryToolBase : : OnShutdown ( ShutdownType ) ;
2020-08-11 01:36:57 -04:00
2021-11-18 14:37:34 -05:00
OutputTypeProperties - > SaveProperties ( this ) ;
2020-08-11 01:36:57 -04:00
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 ;
}
2021-10-26 13:33:36 -04:00
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " RevolveBoundaryToolTransactionName " , " Boundary Revolve Tool " ) ) ;
2020-08-11 01:36:57 -04:00
2021-06-02 15:58:00 -04:00
FCreateMeshObjectParams NewMeshObjectParams ;
2022-01-28 10:18:10 -05:00
NewMeshObjectParams . TargetWorld = GetTargetWorld ( ) ;
2021-06-02 15:58:00 -04:00
NewMeshObjectParams . Transform = ( FTransform ) OpResult . Transform ;
NewMeshObjectParams . BaseName = TEXT ( " Revolve " ) ;
NewMeshObjectParams . Materials . Add ( MaterialProperties - > Material . Get ( ) ) ;
NewMeshObjectParams . SetMesh ( OpResult . Mesh . Get ( ) ) ;
2021-11-18 14:37:34 -05:00
OutputTypeProperties - > ConfigureCreateMeshObjectParams ( NewMeshObjectParams ) ;
2021-06-02 15:58:00 -04:00
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 ,
2021-10-26 13:33:36 -04:00
FRotator ( Settings - > AxisOrientation . X , Settings - > AxisOrientation . Y , 0 ) . Quaternion ( ) ) ) ;
2020-10-09 22:42:26 -04:00
UpdateRevolutionAxis ( ) ;
2020-08-11 01:36:57 -04:00
2021-10-26 13:33:36 -04:00
Cast < IPrimitiveComponentBackedTarget > ( Target ) - > SetOwnerVisibility ( Settings - > bDisplayInputMesh ) ;
2020-08-11 01:36:57 -04:00
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 ( ) ) ) ;
}
2021-10-18 14:21:53 -04:00
Preview - > PreviewMesh - > EnableWireframe ( MaterialProperties - > bShowWireframe ) ;
2020-08-11 01:36:57 -04:00
Preview - > InvalidateResult ( ) ;
}
}
2020-10-09 22:42:26 -04:00
# undef LOCTEXT_NAMESPACE