2020-09-01 14:07:48 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "EdgeLoopInsertionTool.h"
# include "BaseBehaviors/SingleClickBehavior.h"
# include "BaseBehaviors/MouseHoverBehavior.h"
# include "CuttingOps/EdgeLoopInsertionOp.h"
# include "DynamicMeshToMeshDescription.h"
2021-06-13 00:35:22 -04:00
# include "DynamicMesh/DynamicMeshChangeTracker.h"
2020-09-01 14:07:48 -04:00
# include "InteractiveToolManager.h"
# include "MeshDescriptionToDynamicMesh.h"
# include "Operations/GroupEdgeInserter.h"
# include "ToolBuilderUtil.h"
# include "ToolSceneQueriesUtil.h"
# include "ToolSetupUtil.h"
2021-03-18 18:26:33 -04:00
# include "TargetInterfaces/MaterialProvider.h"
# include "TargetInterfaces/MeshDescriptionCommitter.h"
# include "TargetInterfaces/MeshDescriptionProvider.h"
# 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-09-01 14:07:48 -04:00
# define LOCTEXT_NAMESPACE "UEdgeLoopInsertionTool"
2021-03-18 18:26:33 -04:00
USingleSelectionMeshEditingTool * UEdgeLoopInsertionToolBuilder : : CreateNewTool ( const FToolBuilderState & SceneState ) const
2020-09-01 14:07:48 -04:00
{
2021-03-18 18:26:33 -04:00
return NewObject < UEdgeLoopInsertionTool > ( SceneState . ToolManager ) ;
2020-09-01 14:07:48 -04:00
}
TUniquePtr < FDynamicMeshOperator > UEdgeLoopInsertionOperatorFactory : : MakeNewOperator ( )
{
TUniquePtr < FEdgeLoopInsertionOp > Op = MakeUnique < FEdgeLoopInsertionOp > ( ) ;
Op - > OriginalMesh = Tool - > CurrentMesh ;
Op - > OriginalTopology = Tool - > CurrentTopology ;
2021-03-18 18:26:33 -04:00
Op - > SetTransform ( Cast < IPrimitiveComponentBackedTarget > ( Tool - > Target ) - > GetWorldTransform ( ) ) ;
2020-09-01 14:07:48 -04:00
if ( Tool - > bShowingBaseMesh )
{
// Return op with no input lengths so that we get the original mesh back.
return Op ;
}
if ( Tool - > Settings - > InsertionMode = = EEdgeLoopInsertionMode : : PlaneCut )
{
Op - > Mode = FGroupEdgeInserter : : EInsertionMode : : PlaneCut ;
}
else
{
Op - > Mode = FGroupEdgeInserter : : EInsertionMode : : Retriangulate ;
}
Op - > VertexTolerance = Tool - > Settings - > VertexTolerance ;
Op - > GroupEdgeID = Tool - > InputGroupEdgeID ;
Op - > StartCornerID = Tool - > Settings - > bFlipOffsetDirection ?
Op - > OriginalTopology - > Edges [ Op - > GroupEdgeID ] . EndpointCorners . B
: Op - > OriginalTopology - > Edges [ Op - > GroupEdgeID ] . EndpointCorners . A ;
// Set up the inputs
if ( Tool - > Settings - > PositionMode = = EEdgeLoopPositioningMode : : Even )
{
int32 NumLoops = Tool - > Settings - > NumLoops ;
for ( int32 i = 0 ; i < NumLoops ; + + i )
{
Op - > InputLengths . Add ( ( i + 1.0 ) / ( NumLoops + 1.0 ) ) ;
}
}
else if ( Tool - > Settings - > bInteractive )
{
Op - > InputLengths . Add ( Tool - > InteractiveInputLength ) ;
}
else if ( Tool - > Settings - > PositionMode = = EEdgeLoopPositioningMode : : ProportionOffset )
{
Op - > InputLengths . Add ( Tool - > Settings - > ProportionOffset ) ;
}
else
{
Op - > InputLengths . Add ( Tool - > Settings - > DistanceOffset ) ;
}
Op - > bInputsAreProportions = ( Tool - > Settings - > PositionMode = = EEdgeLoopPositioningMode : : Even
| | Tool - > Settings - > PositionMode = = EEdgeLoopPositioningMode : : ProportionOffset ) ;
return Op ;
}
void UEdgeLoopInsertionTool : : Setup ( )
{
USingleSelectionTool : : Setup ( ) ;
2021-03-18 18:26:33 -04:00
if ( ! Target )
2020-09-01 14:07:48 -04:00
{
return ;
}
2021-02-08 17:02:09 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Insert PolyLoop " ) ) ;
2020-09-01 14:07:48 -04:00
GetToolManager ( ) - > DisplayMessage (
2020-10-22 19:19:16 -04:00
LOCTEXT ( " EdgeLoopInsertionToolDescription " , " Click an edge to insert an edge loop passing across that edge. Edge loops follow a sequence of quad-like polygroups. " ) ,
2020-09-01 14:07:48 -04:00
EToolMessageLevel : : UserNotification ) ;
// Initialize the mesh that we'll be operating on
2021-03-29 13:39:32 -04:00
CurrentMesh = MakeShared < FDynamicMesh3 , ESPMode : : ThreadSafe > ( ) ;
2020-09-01 14:07:48 -04:00
FMeshDescriptionToDynamicMesh Converter ;
2021-03-18 18:26:33 -04:00
Converter . Convert ( Cast < IMeshDescriptionProvider > ( Target ) - > GetMeshDescription ( ) , * CurrentMesh ) ;
2021-03-29 13:39:32 -04:00
CurrentTopology = MakeShared < FGroupTopology , ESPMode : : ThreadSafe > ( CurrentMesh . Get ( ) , true ) ;
2020-09-01 14:07:48 -04:00
MeshSpatial . SetMesh ( CurrentMesh . Get ( ) , true ) ;
// Set up properties
Settings = NewObject < UEdgeLoopInsertionProperties > ( this ) ;
Settings - > RestoreProperties ( this ) ;
AddToolPropertySource ( Settings ) ;
// Register ourselves to receive clicks and hover
USingleClickInputBehavior * ClickBehavior = NewObject < USingleClickInputBehavior > ( ) ;
ClickBehavior - > Initialize ( this ) ;
AddInputBehavior ( ClickBehavior ) ;
UMouseHoverBehavior * HoverBehavior = NewObject < UMouseHoverBehavior > ( ) ;
HoverBehavior - > Initialize ( this ) ;
AddInputBehavior ( HoverBehavior ) ;
SetupPreview ( ) ;
2021-03-29 13:39:32 -04:00
// Draws the old group topology
2020-09-01 14:07:48 -04:00
ExistingEdgesRenderer . LineColor = FLinearColor : : Red ;
ExistingEdgesRenderer . LineThickness = 2.0 ;
2021-03-29 13:39:32 -04:00
// Draws the new group edges that are added
2020-09-01 14:07:48 -04:00
PreviewEdgeRenderer . LineColor = FLinearColor : : Green ;
PreviewEdgeRenderer . LineThickness = 2.0 ;
2021-03-29 13:39:32 -04:00
// Highlights non-quad groups that stop the loop;
ProblemTopologyRenderer . LineColor = FLinearColor : : Red ;
ProblemTopologyRenderer . LineThickness = 3.0 ;
ProblemTopologyRenderer . DepthBias = 1.0 ;
2020-09-01 14:07:48 -04:00
// Set up the topology selector, which we use to select the edges where we insert the loops
TopologySelector . Initialize ( CurrentMesh . Get ( ) , CurrentTopology . Get ( ) ) ;
TopologySelector . SetSpatialSource ( [ this ] ( ) { return & MeshSpatial ; } ) ;
2020-10-22 19:19:16 -04:00
TopologySelector . PointsWithinToleranceTest = [ this ] ( const FVector3d & Position1 , const FVector3d & Position2 , double TolScale ) {
2021-03-18 18:26:33 -04:00
UE : : Geometry : : FTransform3d Transform ( Cast < IPrimitiveComponentBackedTarget > ( Target ) - > GetWorldTransform ( ) ) ;
2020-10-22 19:19:16 -04:00
return ToolSceneQueriesUtil : : PointSnapQuery ( CameraState , Transform . TransformPosition ( Position1 ) , Transform . TransformPosition ( Position2 ) ,
ToolSceneQueriesUtil : : GetDefaultVisualAngleSnapThreshD ( ) * TolScale ) ;
2020-09-01 14:07:48 -04:00
} ;
TopologySelectorSettings . bEnableEdgeHits = true ;
TopologySelectorSettings . bEnableFaceHits = false ;
TopologySelectorSettings . bEnableCornerHits = false ;
}
void UEdgeLoopInsertionTool : : SetupPreview ( )
{
UEdgeLoopInsertionOperatorFactory * OpFactory = NewObject < UEdgeLoopInsertionOperatorFactory > ( ) ;
OpFactory - > Tool = this ;
Preview = NewObject < UMeshOpPreviewWithBackgroundCompute > ( OpFactory ) ;
Preview - > Setup ( TargetWorld , OpFactory ) ;
2021-06-11 22:39:18 -04:00
Preview - > PreviewMesh - > SetTangentsMode ( EDynamicMeshComponentTangentsMode : : AutoCalculated ) ;
2020-09-01 14:07:48 -04:00
FComponentMaterialSet MaterialSet ;
2021-03-18 18:26:33 -04:00
Cast < IMaterialProvider > ( Target ) - > GetMaterialSet ( MaterialSet ) ;
2020-09-01 14:07:48 -04:00
Preview - > ConfigureMaterials ( MaterialSet . Materials , ToolSetupUtil : : GetDefaultWorkingMaterial ( GetToolManager ( ) ) ) ;
// Whenever we get a new result from the op, we need to extract the preview edges so that
2021-03-29 13:39:32 -04:00
// we can draw them if we want to, and the additional outputs we need (changed triangles and
// topology).
2020-09-01 14:07:48 -04:00
Preview - > OnOpCompleted . AddLambda ( [ this ] ( const FDynamicMeshOperator * UncastOp ) {
const FEdgeLoopInsertionOp * Op = static_cast < const FEdgeLoopInsertionOp * > ( UncastOp ) ;
bLastComputeSucceeded = Op - > bSucceeded ;
LatestOpTopologyResult . Reset ( ) ;
PreviewEdges . Reset ( ) ;
2021-03-29 13:39:32 -04:00
LatestOpChangedTids . Reset ( ) ;
2020-09-01 14:07:48 -04:00
if ( bLastComputeSucceeded )
{
Op - > GetLoopEdgeLocations ( PreviewEdges ) ;
LatestOpTopologyResult = Op - > ResultTopology ;
2021-03-29 13:39:32 -04:00
LatestOpChangedTids = Op - > ChangedTids ;
}
// Regardless of success, extract things for highlighting any non-quads that stopped our loop.
ProblemTopologyEdges . Reset ( ) ;
ProblemTopologyVerts . Reset ( ) ;
for ( int32 GroupEdgeID : Op - > ProblemGroupEdgeIDs )
{
for ( int32 Eid : CurrentTopology - > GetGroupEdgeEdges ( GroupEdgeID ) )
{
TPair < FVector3d , FVector3d > Endpoints ;
CurrentMesh - > GetEdgeV ( Eid , Endpoints . Key , Endpoints . Value ) ;
ProblemTopologyEdges . Add ( MoveTemp ( Endpoints ) ) ;
}
FGroupTopology : : FGroupEdge & GroupEdge = CurrentTopology - > Edges [ GroupEdgeID ] ;
if ( GroupEdge . EndpointCorners . A ! = FDynamicMesh3 : : InvalidID )
{
ProblemTopologyVerts . AddUnique ( CurrentMesh - > GetVertex ( CurrentTopology - > Corners [ GroupEdge . EndpointCorners . A ] . VertexID ) ) ;
ProblemTopologyVerts . AddUnique ( CurrentMesh - > GetVertex ( CurrentTopology - > Corners [ GroupEdge . EndpointCorners . B ] . VertexID ) ) ;
}
2020-09-01 14:07:48 -04:00
}
2020-09-24 00:43:27 -04:00
} ) ;
2021-03-29 13:39:32 -04:00
2020-09-24 00:43:27 -04:00
// In case of failure, we want to hide the broken preview, since we wouldn't accept it on
// a click. Note that this can't be fired OnOpCompleted because the preview is updated
// with the op result after that callback, which would undo the reset. The preview edge
// extraction can't be lumped in here because it needs the op rather than the preview object.
Preview - > OnMeshUpdated . AddLambda ( [ this ] ( UMeshOpPreviewWithBackgroundCompute * ) {
if ( ! bLastComputeSucceeded )
2020-09-01 14:07:48 -04:00
{
Preview - > PreviewMesh - > UpdatePreview ( CurrentMesh . Get ( ) ) ;
}
} ) ;
// Set initial preview to unprocessed mesh, so that things don't disappear initially
2021-03-18 18:26:33 -04:00
IPrimitiveComponentBackedTarget * TargetComponent = Cast < IPrimitiveComponentBackedTarget > ( Target ) ;
2020-09-01 14:07:48 -04:00
Preview - > PreviewMesh - > UpdatePreview ( CurrentMesh . Get ( ) ) ;
2021-03-18 18:26:33 -04:00
Preview - > PreviewMesh - > SetTransform ( TargetComponent - > GetWorldTransform ( ) ) ;
2020-09-01 14:07:48 -04:00
Preview - > PreviewMesh - > EnableWireframe ( Settings - > bWireframe ) ;
Preview - > SetVisibility ( true ) ;
ClearPreview ( ) ;
2021-03-18 18:26:33 -04:00
TargetComponent - > SetOwnerVisibility ( false ) ;
2020-09-01 14:07:48 -04:00
}
void UEdgeLoopInsertionTool : : Shutdown ( EToolShutdownType ShutdownType )
{
2021-03-29 13:39:32 -04:00
// Set visibility before committing so that it doesn't get saved as false.
Cast < IPrimitiveComponentBackedTarget > ( Target ) - > SetOwnerVisibility ( true ) ;
if ( ShutdownType = = EToolShutdownType : : Accept )
{
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " EdgeLoopInsertionToolTransactionName " , " Edge Loop Tool " ) ) ;
Cast < IMeshDescriptionCommitter > ( Target ) - > CommitMeshDescription ( [ this ] ( const IMeshDescriptionCommitter : : FCommitterParams & CommitParams )
{
FDynamicMeshToMeshDescription Converter ;
Converter . Convert ( CurrentMesh . Get ( ) , * CommitParams . MeshDescriptionOut ) ;
} ) ;
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
2020-09-01 14:07:48 -04:00
Settings - > SaveProperties ( this ) ;
Preview - > Shutdown ( ) ;
CurrentMesh . Reset ( ) ;
2020-09-24 00:43:27 -04:00
CurrentTopology . Reset ( ) ;
2020-09-01 14:07:48 -04:00
ExpireChanges ( ) ;
}
void UEdgeLoopInsertionTool : : OnTick ( float DeltaTime )
{
if ( Preview )
{
Preview - > Tick ( DeltaTime ) ;
if ( bWaitingForInsertionCompletion & & Preview - > HaveValidResult ( ) )
{
if ( bLastComputeSucceeded )
{
2021-03-29 13:39:32 -04:00
FDynamicMeshChangeTracker ChangeTracker ( CurrentMesh . Get ( ) ) ;
ChangeTracker . BeginChange ( ) ;
ChangeTracker . SaveTriangles ( * LatestOpChangedTids , true /*bSaveVertices*/ ) ;
2020-09-01 14:07:48 -04:00
// Update current mesh and topology
CurrentMesh - > Copy ( * Preview - > PreviewMesh - > GetMesh ( ) , true , true , true , true ) ;
* CurrentTopology = * LatestOpTopologyResult ;
CurrentTopology - > RetargetOnClonedMesh ( CurrentMesh . Get ( ) ) ;
MeshSpatial . Build ( ) ;
TopologySelector . Invalidate ( true , true ) ;
2021-03-29 13:39:32 -04:00
// Emit transaction
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " EdgeLoopInsertionTransactionName " , " Edge Loop Insertion " ) ) ;
GetToolManager ( ) - > EmitObjectChange ( this , MakeUnique < FEdgeLoopInsertionChange > ( ChangeTracker . EndChange ( ) , CurrentChangeStamp ) ,
LOCTEXT ( " EdgeLoopInsertion " , " Edge Loop Insertion " ) ) ;
GetToolManager ( ) - > EndUndoTransaction ( ) ;
2020-09-01 14:07:48 -04:00
}
PreviewEdges . Reset ( ) ;
2021-03-29 13:39:32 -04:00
ProblemTopologyEdges . Reset ( ) ;
ProblemTopologyVerts . Reset ( ) ;
2020-09-01 14:07:48 -04:00
bWaitingForInsertionCompletion = false ;
}
}
}
void UEdgeLoopInsertionTool : : Render ( IToolsContextRenderAPI * RenderAPI )
{
GetToolManager ( ) - > GetContextQueriesAPI ( ) - > GetCurrentViewState ( CameraState ) ;
// Draw the existing group edges
FViewCameraState RenderCameraState = RenderAPI - > GetCameraState ( ) ;
ExistingEdgesRenderer . BeginFrame ( RenderAPI , RenderCameraState ) ;
ExistingEdgesRenderer . SetTransform ( Preview - > PreviewMesh - > GetTransform ( ) ) ;
for ( const FGroupTopology : : FGroupEdge & Edge : CurrentTopology - > Edges )
{
FVector3d A , B ;
for ( int32 eid : Edge . Span . Edges )
{
CurrentMesh - > GetEdgeV ( eid , A , B ) ;
ExistingEdgesRenderer . DrawLine ( A , B ) ;
}
}
ExistingEdgesRenderer . EndFrame ( ) ;
// Draw the preview edges
PreviewEdgeRenderer . BeginFrame ( RenderAPI , RenderCameraState ) ;
2020-09-24 00:43:27 -04:00
PreviewEdgeRenderer . SetTransform ( Preview - > PreviewMesh - > GetTransform ( ) ) ;
2020-09-01 14:07:48 -04:00
for ( TPair < FVector3d , FVector3d > & EdgeVerts : PreviewEdges )
{
PreviewEdgeRenderer . DrawLine ( EdgeVerts . Key , EdgeVerts . Value ) ;
}
PreviewEdgeRenderer . EndFrame ( ) ;
2021-03-29 13:39:32 -04:00
if ( Settings - > bHighlightProblemGroups )
{
// Highlight any non-quad groups that stopped the loop.
ProblemTopologyRenderer . BeginFrame ( RenderAPI , RenderCameraState ) ;
ProblemTopologyRenderer . SetTransform ( Preview - > PreviewMesh - > GetTransform ( ) ) ;
for ( TPair < FVector3d , FVector3d > & EdgeVerts : ProblemTopologyEdges )
{
ProblemTopologyRenderer . DrawLine ( EdgeVerts . Key , EdgeVerts . Value ) ;
}
for ( FVector3d & Vert : ProblemTopologyVerts )
{
ProblemTopologyRenderer . DrawViewFacingX ( Vert , ProblemVertTickWidth ) ;
}
ProblemTopologyRenderer . EndFrame ( ) ;
}
2020-09-01 14:07:48 -04:00
}
bool UEdgeLoopInsertionTool : : CanAccept ( ) const
{
return ! bWaitingForInsertionCompletion ;
}
void UEdgeLoopInsertionTool : : OnPropertyModified ( UObject * PropertySet , FProperty * Property )
{
PreviewEdges . Reset ( ) ;
Preview - > PreviewMesh - > EnableWireframe ( Settings - > bWireframe ) ;
Preview - > InvalidateResult ( ) ;
}
FInputRayHit UEdgeLoopInsertionTool : : HitTest ( const FRay & WorldRay )
{
FInputRayHit Hit ;
// See if we hit an edge
2021-03-30 21:25:22 -04:00
FTransform3d LocalToWorld ( Cast < IPrimitiveComponentBackedTarget > ( Target ) - > GetWorldTransform ( ) ) ;
FRay3d LocalRay ( LocalToWorld . InverseTransformPosition ( ( FVector3d ) WorldRay . Origin ) ,
LocalToWorld . InverseTransformVector ( ( FVector3d ) WorldRay . Direction ) , false ) ;
2020-09-01 14:07:48 -04:00
FGroupTopologySelection Selection ;
FVector3d Position , Normal ;
if ( TopologySelector . FindSelectedElement (
TopologySelectorSettings , LocalRay , Selection , Position , Normal ) )
{
// TODO: We could check here that the edge has some quad-like neighbor. For now we
// just check that the edge isn't a loop unto itself (in which case the neighbor groups
// are definitely not quad-like).
2020-09-24 00:43:27 -04:00
int32 GroupEdgeID = Selection . GetASelectedEdgeID ( ) ;
2020-09-01 14:07:48 -04:00
const FGroupTopology : : FGroupEdge & GroupEdge = CurrentTopology - > Edges [ GroupEdgeID ] ;
if ( GroupEdge . EndpointCorners . A ! = FDynamicMesh3 : : InvalidID )
{
Hit = FInputRayHit ( LocalRay . Project ( Position ) ) ;
}
}
return Hit ;
}
bool UEdgeLoopInsertionTool : : UpdateHoveredItem ( const FRay & WorldRay )
{
// Check that we hit an edge
2021-03-30 21:25:22 -04:00
FTransform3d LocalToWorld ( Cast < IPrimitiveComponentBackedTarget > ( Target ) - > GetWorldTransform ( ) ) ;
FRay3d LocalRay ( LocalToWorld . InverseTransformPosition ( ( FVector3d ) WorldRay . Origin ) ,
LocalToWorld . InverseTransformVector ( ( FVector3d ) WorldRay . Direction ) , false ) ;
2020-09-01 14:07:48 -04:00
FGroupTopologySelection Selection ;
FVector3d Position , Normal ;
int32 EdgeSegmentID ;
if ( ! TopologySelector . FindSelectedElement (
TopologySelectorSettings , LocalRay , Selection , Position , Normal , & EdgeSegmentID ) )
{
ClearPreview ( ) ;
return false ; // Didn't hit anything
}
// Check that the edge has endpoints
2020-09-24 00:43:27 -04:00
int32 GroupEdgeID = Selection . GetASelectedEdgeID ( ) ;
2020-09-01 14:07:48 -04:00
FGroupTopology : : FGroupEdge GroupEdge = CurrentTopology - > Edges [ GroupEdgeID ] ;
if ( GroupEdge . EndpointCorners . A = = FDynamicMesh3 : : InvalidID )
{
ClearPreview ( ) ;
return false ; // Edge definitely doesn't have quad-like neighbors
}
if ( Settings - > PositionMode = = EEdgeLoopPositioningMode : : Even )
{
// In even mode and non-interactive mode, all that matters is the group edge
// that we're hovering, not where our pointer is exactly.
ConditionallyUpdatePreview ( GroupEdgeID ) ;
return true ;
}
if ( ! Settings - > bInteractive )
{
// Don't try to insert a loop when our inputs don't make sense.
double TotalLength = CurrentTopology - > GetEdgeArcLength ( GroupEdgeID ) ;
if ( Settings - > PositionMode = = EEdgeLoopPositioningMode : : DistanceOffset )
{
if ( Settings - > DistanceOffset > TotalLength | | Settings - > DistanceOffset < = Settings - > VertexTolerance )
{
ClearPreview ( ) ;
return false ;
}
}
else if ( Settings - > PositionMode = = EEdgeLoopPositioningMode : : ProportionOffset )
{
if ( abs ( Settings - > ProportionOffset * TotalLength - TotalLength ) < = Settings - > VertexTolerance )
{
ClearPreview ( ) ;
return false ;
}
}
ConditionallyUpdatePreview ( GroupEdgeID ) ;
return true ;
}
// Otherwise, we need to figure out where along the edge we are hovering.
double NewInputLength = 0 ;
int32 StartVid = GroupEdge . Span . Vertices [ EdgeSegmentID ] ;
int32 EndVid = GroupEdge . Span . Vertices [ EdgeSegmentID + 1 ] ;
FVector3d StartVert = CurrentMesh - > GetVertex ( StartVid ) ;
FVector3d EndVert = CurrentMesh - > GetVertex ( EndVid ) ;
FRay EdgeRay ( ( FVector ) StartVert , ( FVector ) ( EndVert - StartVert ) , false ) ;
double DistDownEdge = EdgeRay . GetParameter ( ( FVector ) Position ) ;
TArray < double > PerVertexLengths ;
double TotalLength = CurrentTopology - > GetEdgeArcLength ( GroupEdgeID , & PerVertexLengths ) ;
NewInputLength = PerVertexLengths [ EdgeSegmentID ] + DistDownEdge ;
if ( Settings - > bFlipOffsetDirection )
{
// If we flipped start corner, we should be measuring from the opposite direction
NewInputLength = TotalLength - NewInputLength ;
}
// We avoid trying to insert loops that are guaranteed to follow existing group edges.
// Distance offset with total length may work if the group widens on the other side.
// Though it's worth noting that this filter as a whole is assuming straight group edges...
if ( NewInputLength < = Settings - > VertexTolerance | |
( Settings - > PositionMode = = EEdgeLoopPositioningMode : : ProportionOffset
& & abs ( NewInputLength - TotalLength ) < = Settings - > VertexTolerance ) )
{
ClearPreview ( ) ;
return false ;
}
if ( Settings - > PositionMode = = EEdgeLoopPositioningMode : : ProportionOffset )
{
NewInputLength / = TotalLength ;
}
ConditionallyUpdatePreview ( GroupEdgeID , & NewInputLength ) ;
return true ;
}
void UEdgeLoopInsertionTool : : ClearPreview ( )
{
// We don't seem to have a way to cancel the background op on a mesh without shutting down
// the entire preview, hence us clearing the preview this way. When we know that the op is
// not running, we can instead use UpdatePreview() to reset the mesh to the original mesh.
bShowingBaseMesh = true ;
PreviewEdges . Reset ( ) ;
Preview - > InvalidateResult ( ) ;
}
void UEdgeLoopInsertionTool : : ConditionallyUpdatePreview ( int32 NewGroupID , double * NewInteractiveInputLength )
{
if ( bShowingBaseMesh | | InputGroupEdgeID ! = NewGroupID
| | ( NewInteractiveInputLength & & Settings - > PositionMode ! = EEdgeLoopPositioningMode : : Even
& & * NewInteractiveInputLength ! = InteractiveInputLength ) )
{
InputGroupEdgeID = NewGroupID ;
if ( NewInteractiveInputLength )
{
InteractiveInputLength = * NewInteractiveInputLength ;
}
bShowingBaseMesh = false ;
PreviewEdges . Reset ( ) ;
Preview - > InvalidateResult ( ) ;
}
}
FInputRayHit UEdgeLoopInsertionTool : : BeginHoverSequenceHitTest ( const FInputDeviceRay & PressPos )
{
if ( bWaitingForInsertionCompletion )
{
return FInputRayHit ( ) ;
}
return HitTest ( PressPos . WorldRay ) ;
}
bool UEdgeLoopInsertionTool : : OnUpdateHover ( const FInputDeviceRay & DevicePos )
{
if ( bWaitingForInsertionCompletion )
{
return false ;
}
return UpdateHoveredItem ( DevicePos . WorldRay ) ;
}
void UEdgeLoopInsertionTool : : OnEndHover ( )
{
if ( ! bWaitingForInsertionCompletion )
{
ClearPreview ( ) ;
}
}
FInputRayHit UEdgeLoopInsertionTool : : IsHitByClick ( const FInputDeviceRay & ClickPos )
{
FInputRayHit Hit ;
if ( bWaitingForInsertionCompletion )
{
return Hit ;
}
return HitTest ( ClickPos . WorldRay ) ;
}
void UEdgeLoopInsertionTool : : OnClicked ( const FInputDeviceRay & ClickPos )
{
if ( bWaitingForInsertionCompletion )
{
return ;
}
if ( UpdateHoveredItem ( ClickPos . WorldRay ) )
{
bWaitingForInsertionCompletion = true ;
}
}
// Undo/redo support
2021-03-29 13:39:32 -04:00
void FEdgeLoopInsertionChange : : Apply ( UObject * Object )
2020-09-01 14:07:48 -04:00
{
2021-03-29 13:39:32 -04:00
UEdgeLoopInsertionTool * Tool = Cast < UEdgeLoopInsertionTool > ( Object ) ;
MeshChange - > Apply ( Tool - > CurrentMesh . Get ( ) , false ) ;
Tool - > MeshSpatial . Build ( ) ;
Tool - > TopologySelector . Invalidate ( true , true ) ;
Tool - > CurrentTopology - > RebuildTopology ( ) ;
Tool - > ClearPreview ( ) ;
2020-09-01 14:07:48 -04:00
}
2021-03-29 13:39:32 -04:00
void FEdgeLoopInsertionChange : : Revert ( UObject * Object )
2020-09-01 14:07:48 -04:00
{
2021-03-29 13:39:32 -04:00
UEdgeLoopInsertionTool * Tool = Cast < UEdgeLoopInsertionTool > ( Object ) ;
MeshChange - > Apply ( Tool - > CurrentMesh . Get ( ) , true ) ;
Tool - > MeshSpatial . Build ( ) ;
Tool - > TopologySelector . Invalidate ( true , true ) ;
Tool - > CurrentTopology - > RebuildTopology ( ) ;
Tool - > ClearPreview ( ) ;
2020-09-01 14:07:48 -04:00
}
# undef LOCTEXT_NAMESPACE