2020-01-27 20:11:15 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "DrawPolyPathTool.h"
# include "InteractiveToolManager.h"
# include "ToolBuilderUtil.h"
# include "BaseBehaviors/SingleClickBehavior.h"
# include "BaseBehaviors/MouseHoverBehavior.h"
# include "ToolSceneQueriesUtil.h"
# include "Util/ColorConstants.h"
# include "ToolSetupUtil.h"
2021-05-22 01:32:46 -04:00
# include "DynamicMesh/MeshIndexUtil.h"
2021-11-18 14:37:34 -05:00
# include "Generators/PolygonEdgeMeshGenerator.h"
2020-01-27 20:11:15 -05:00
# include "Distance/DistLine3Line3.h"
2021-06-02 15:58:00 -04:00
# include "ModelingObjectsCreationAPI.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/MeshTransforms.h"
2020-01-27 20:11:15 -05:00
# include "Selection/ToolSelectionUtil.h"
# include "Operations/ExtrudeMesh.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/MeshNormals.h"
2022-08-25 11:51:02 -04:00
# include "DynamicMesh/MeshTangents.h"
2021-10-13 12:32:03 -04:00
# include "MeshBoundaryLoops.h"
# include "ToolDataVisualizer.h"
2020-01-27 20:11:15 -05:00
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(DrawPolyPathTool)
2021-03-09 19:33:56 -04:00
using namespace UE : : Geometry ;
2020-01-27 20:11:15 -05:00
2021-03-09 19:33:56 -04:00
# define LOCTEXT_NAMESPACE "UDrawPolyPathTool"
2020-01-27 20:11:15 -05:00
2021-11-18 14:37:34 -05:00
namespace DrawPolyPathToolLocals
2020-03-09 13:52:38 -04:00
{
2021-11-18 14:37:34 -05:00
void ComputeArcLengths ( const TArray < FFrame3d > & PathPoints , TArray < double > & ArcLengths )
2020-03-09 13:52:38 -04:00
{
2021-11-18 14:37:34 -05:00
double CurPathLength = 0 ;
ArcLengths . SetNum ( PathPoints . Num ( ) ) ;
ArcLengths [ 0 ] = 0.0f ;
for ( int32 k = 1 ; k < PathPoints . Num ( ) ; + + k )
2020-03-09 13:52:38 -04:00
{
2021-11-18 14:37:34 -05:00
CurPathLength + = Distance ( PathPoints [ k ] . Origin , PathPoints [ k - 1 ] . Origin ) ;
ArcLengths [ k ] = CurPathLength ;
}
}
2022-09-09 01:37:45 -04:00
/// Generate path mesh
/// @return Offset to location of generated mesh
2024-01-12 10:47:04 -05:00
[[nodiscard]] FVector3d GeneratePathMesh ( FDynamicMesh3 & Mesh ,
2021-12-10 18:05:44 -05:00
const TArray < FFrame3d > & InPathPoints ,
const TArray < double > & InOffsetScaleFactors ,
double OffsetDistance ,
bool bPathIsClosed ,
bool bRampMode ,
bool bSinglePolyGroup ,
bool bRoundedCorners ,
double CornerRadiusFraction ,
bool bLimitCornerRadius ,
int NumCornerArcPoints )
2021-11-18 14:37:34 -05:00
{
Mesh . Clear ( ) ;
TArray < FFrame3d > UsePathPoints = InPathPoints ;
TArray < double > UseOffsetScaleFactors = InOffsetScaleFactors ;
2022-09-09 01:37:45 -04:00
// re-center the input points at the origin
FVector3d Center ( 0 , 0 , 0 ) ;
if ( UsePathPoints . Num ( ) )
{
FAxisAlignedBox3d PathBounds ;
for ( const FFrame3d & Point : UsePathPoints )
{
PathBounds . Contain ( Point . Origin ) ;
}
Center = PathBounds . Center ( ) ;
for ( FFrame3d & Point : UsePathPoints )
{
Point . Origin - = Center ;
}
}
2021-11-18 14:37:34 -05:00
if ( bPathIsClosed & & bRampMode )
{
// Duplicate vertices at the beginning/end of the path when generating a ramp
const FFrame3d FirstPoint = InPathPoints [ 0 ] ;
UsePathPoints . Add ( FirstPoint ) ;
const double FirstScaleFactor = InOffsetScaleFactors [ 0 ] ;
UseOffsetScaleFactors . Add ( FirstScaleFactor ) ;
2020-03-09 13:52:38 -04:00
}
2021-11-18 14:37:34 -05:00
const int NumPoints = UsePathPoints . Num ( ) ;
2020-03-09 13:52:38 -04:00
2021-11-18 14:37:34 -05:00
TArray < double > ArcLengths ;
ComputeArcLengths ( UsePathPoints , ArcLengths ) ;
2021-12-10 18:05:44 -05:00
double PathLength = ArcLengths . Last ( ) ;
2021-11-18 14:37:34 -05:00
2022-01-10 13:21:53 -05:00
const double PathWidth = 2.0 * OffsetDistance ;
const double CornerRadius = CornerRadiusFraction * PathWidth ;
FPolygonEdgeMeshGenerator MeshGen ( UsePathPoints , bPathIsClosed , UseOffsetScaleFactors , PathWidth , FVector3d : : UnitZ ( ) , bRoundedCorners , CornerRadius , bLimitCornerRadius , NumCornerArcPoints ) ;
MeshGen . bSinglePolyGroup = bSinglePolyGroup ;
MeshGen . UVWidth = PathLength ;
MeshGen . UVHeight = 2 * OffsetDistance ;
MeshGen . Generate ( ) ;
Mesh . Copy ( & MeshGen ) ;
Mesh . EnableVertexUVs ( FVector2f : : Zero ( ) ) ;
if ( bRampMode )
2020-03-09 13:52:38 -04:00
{
2022-01-10 13:21:53 -05:00
// Temporarily set vertex UVs to arclengths, for use in interpolating height in ramp mode
2020-03-09 13:52:38 -04:00
2022-01-10 13:21:53 -05:00
if ( bRoundedCorners )
2020-03-09 13:52:38 -04:00
{
2022-01-10 13:21:53 -05:00
// If we added arcs to the corners, recompute arc lengths
const int N = Mesh . VertexCount ( ) / 2 ;
ArcLengths . Init ( 0.0 , N ) ;
double CurPathLength = 0 ;
for ( int k = 1 ; k < N ; + + k )
2021-12-10 18:05:44 -05:00
{
2022-01-10 13:21:53 -05:00
CurPathLength + = Distance ( Mesh . GetVertex ( 2 * k ) , Mesh . GetVertex ( 2 * ( k - 1 ) ) ) ;
ArcLengths [ k ] = CurPathLength ;
2021-12-10 18:05:44 -05:00
}
2022-01-10 13:21:53 -05:00
PathLength = ArcLengths . Last ( ) ;
}
2021-12-10 18:05:44 -05:00
2022-01-10 13:21:53 -05:00
int NumMeshVertices = Mesh . VertexCount ( ) ;
ensure ( NumMeshVertices = = Mesh . MaxVertexID ( ) ) ;
ensure ( NumMeshVertices = = 2 * ArcLengths . Num ( ) ) ;
2021-12-10 18:05:44 -05:00
2022-01-10 13:21:53 -05:00
for ( int k = 0 ; k < NumMeshVertices / 2 ; + + k )
{
const float Alpha = static_cast < float > ( ArcLengths [ k ] / PathLength ) ;
Mesh . SetVertexUV ( 2 * k , FVector2f ( Alpha , static_cast < float > ( k ) ) ) ;
Mesh . SetVertexUV ( 2 * k + 1 , FVector2f ( Alpha , static_cast < float > ( k ) ) ) ;
}
2021-12-10 18:05:44 -05:00
2022-01-10 13:21:53 -05:00
if ( bPathIsClosed )
{
2021-12-10 18:05:44 -05:00
// Set last vertex positions to match first vertex locations so we can construct the vertical wall
2021-11-18 14:37:34 -05:00
Mesh . SetVertex ( NumMeshVertices - 2 , Mesh . GetVertex ( 0 ) ) ;
Mesh . SetVertex ( NumMeshVertices - 1 , Mesh . GetVertex ( 1 ) ) ;
2020-03-09 13:52:38 -04:00
}
}
2022-09-09 01:37:45 -04:00
return Center ;
2021-11-18 14:37:34 -05:00
}
} // namespace DrawPolyPathToolLocals
2020-03-09 13:52:38 -04:00
2020-01-27 20:11:15 -05:00
/*
* ToolBuilder
*/
bool UDrawPolyPathToolBuilder : : CanBuildTool ( const FToolBuilderState & SceneState ) const
{
2021-06-02 15:58:00 -04:00
return true ;
2020-01-27 20:11:15 -05:00
}
UInteractiveTool * UDrawPolyPathToolBuilder : : BuildTool ( const FToolBuilderState & SceneState ) const
{
UDrawPolyPathTool * NewTool = NewObject < UDrawPolyPathTool > ( SceneState . ToolManager ) ;
NewTool - > SetWorld ( SceneState . World ) ;
2024-04-23 18:39:48 -04:00
NewTool - > SetInitialDrawFrame ( ToolSetupUtil : : GetDefaultWorldReferenceFrame ( SceneState . ToolManager ) ) ;
2020-01-27 20:11:15 -05:00
return NewTool ;
}
/*
* Tool methods
*/
void UDrawPolyPathTool : : SetWorld ( UWorld * World )
{
this - > TargetWorld = World ;
}
void UDrawPolyPathTool : : Setup ( )
{
UInteractiveTool : : Setup ( ) ;
// register click behavior
USingleClickInputBehavior * ClickBehavior = NewObject < USingleClickInputBehavior > ( this ) ;
ClickBehavior - > Initialize ( this ) ;
2022-01-10 15:56:01 -05:00
ClickBehavior - > Modifiers . RegisterModifier ( ShiftModifierID , FInputDeviceState : : IsShiftKeyDown ) ;
2020-01-27 20:11:15 -05:00
AddInputBehavior ( ClickBehavior ) ;
UMouseHoverBehavior * HoverBehavior = NewObject < UMouseHoverBehavior > ( this ) ;
HoverBehavior - > Initialize ( this ) ;
2022-01-10 15:56:01 -05:00
HoverBehavior - > Modifiers . RegisterModifier ( ShiftModifierID , FInputDeviceState : : IsShiftKeyDown ) ;
2020-01-27 20:11:15 -05:00
AddInputBehavior ( HoverBehavior ) ;
PlaneMechanic = NewObject < UConstructionPlaneMechanic > ( this ) ;
PlaneMechanic - > Setup ( this ) ;
PlaneMechanic - > CanUpdatePlaneFunc = [ this ] ( ) { return CanUpdateDrawPlane ( ) ; } ;
PlaneMechanic - > Initialize ( TargetWorld , DrawPlaneWorld ) ;
PlaneMechanic - > UpdateClickPriority ( ClickBehavior - > GetPriority ( ) . MakeHigher ( ) ) ;
PlaneMechanic - > OnPlaneChanged . AddLambda ( [ this ] ( ) {
DrawPlaneWorld = PlaneMechanic - > Plane ;
UpdateSurfacePathPlane ( ) ;
} ) ;
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
OutputTypeProperties = NewObject < UCreateMeshObjectTypeProperties > ( this ) ;
OutputTypeProperties - > RestoreProperties ( this ) ;
OutputTypeProperties - > InitializeDefault ( ) ;
OutputTypeProperties - > WatchProperty ( OutputTypeProperties - > OutputType , [ this ] ( FString ) { OutputTypeProperties - > UpdatePropertyVisibility ( ) ; } ) ;
AddToolPropertySource ( OutputTypeProperties ) ;
2020-01-27 20:11:15 -05:00
// add properties
TransformProps = NewObject < UDrawPolyPathProperties > ( this ) ;
TransformProps - > RestoreProperties ( this ) ;
AddToolPropertySource ( TransformProps ) ;
2022-01-10 15:56:01 -05:00
TransformProps - > WatchProperty ( TransformProps - > WidthMode , [ this ] ( EDrawPolyPathWidthMode Mode )
2021-10-25 20:05:28 -04:00
{
2022-01-10 15:56:01 -05:00
if ( State = = EState : : SettingWidth )
2021-10-25 20:05:28 -04:00
{
2022-01-10 15:56:01 -05:00
// Switch to the other mode of setting width.
BeginSettingWidth ( ) ;
}
else if ( State ! = EState : : DrawingPath )
{
UpdatePathPreview ( ) ;
2021-10-25 20:05:28 -04:00
}
} ) ;
2022-01-10 15:56:01 -05:00
TransformProps - > WatchProperty ( TransformProps - > RadiusMode , [ this ] ( EDrawPolyPathRadiusMode Mode )
2021-11-07 23:43:01 -05:00
{
2022-01-10 15:56:01 -05:00
if ( State = = EState : : SettingRadius )
2021-11-07 23:43:01 -05:00
{
2022-01-10 15:56:01 -05:00
// Switch to the other mode of setting radius.
BeginSettingRadius ( ) ;
}
else if ( State ! = EState : : DrawingPath )
{
UpdatePathPreview ( ) ;
}
} ) ;
TransformProps - > WatchProperty ( TransformProps - > ExtrudeMode , [ this ] ( EDrawPolyPathExtrudeMode Mode )
{
if ( State = = EState : : SettingHeight )
{
BeginSettingHeight ( ) ;
2021-11-07 23:43:01 -05:00
}
} ) ;
2020-01-27 20:11:15 -05:00
ExtrudeProperties = NewObject < UDrawPolyPathExtrudeProperties > ( ) ;
ExtrudeProperties - > RestoreProperties ( this ) ;
AddToolPropertySource ( ExtrudeProperties ) ;
SetToolPropertySourceEnabled ( ExtrudeProperties , false ) ;
// initialize material properties for new objects
MaterialProperties = NewObject < UNewMeshMaterialProperties > ( this ) ;
MaterialProperties - > RestoreProperties ( this ) ;
MaterialProperties - > bShowExtendedOptions = false ;
AddToolPropertySource ( MaterialProperties ) ;
// begin path draw
InitializeNewSurfacePath ( ) ;
2021-10-21 17:57:49 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Path Extrude " ) ) ;
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : Shutdown ( EToolShutdownType ShutdownType )
{
2022-01-10 15:56:01 -05:00
// If we have a ready result, go ahead and create it, because especially with "fixed"
// height mode, it's easy to think that Enter should result in its creation.
// TODO: We could consider letting this tool accept nested accept/cancel commands, but
// it's a bit more work.
if ( State = = EState : : SettingHeight & & ShutdownType ! = EToolShutdownType : : Cancel )
{
2023-03-09 17:33:32 -05:00
CurHeight = TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : Flat ? 0.0 : TransformProps - > ExtrudeHeight ;
2022-01-10 15:56:01 -05:00
OnCompleteExtrudeHeight ( ) ;
}
2021-11-23 10:39:20 -05:00
if ( bHasSavedWidth )
{
TransformProps - > Width = SavedWidth ;
bHasSavedWidth = false ;
}
2022-01-10 15:56:01 -05:00
if ( bHasSavedRadius )
{
TransformProps - > CornerRadius = SavedRadius ;
bHasSavedRadius = false ;
}
2021-11-23 10:39:20 -05:00
if ( bHasSavedExtrudeHeight )
{
TransformProps - > ExtrudeHeight = SavedExtrudeHeight ;
SavedExtrudeHeight = false ;
}
2020-01-27 20:11:15 -05:00
PlaneMechanic - > Shutdown ( ) ;
PlaneMechanic = nullptr ;
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
OutputTypeProperties - > SaveProperties ( this ) ;
2020-01-27 20:11:15 -05:00
TransformProps - > SaveProperties ( this ) ;
ExtrudeProperties - > SaveProperties ( this ) ;
MaterialProperties - > SaveProperties ( this ) ;
ClearPreview ( ) ;
}
2022-01-10 15:56:01 -05:00
void UDrawPolyPathTool : : OnPropertyModified ( UObject * PropertySet , FProperty * Property )
{
if ( ! ensure ( Property ) )
{
// Not sure whether this would ever happen, but just in case
return ;
}
// We deal with these properties here instead of inside a watcher because we frequently update them
// ourselves as we set them interactively, and we don't want to bother with doing silent watcher updates.
// Instead we rely on the fact that user scrubbing of the properties calls OnPropertyModified whereas
// programmatic changes generally don't.
if ( Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UDrawPolyPathProperties , Width )
| | Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UDrawPolyPathProperties , CornerRadius ) )
{
UpdatePathPreview ( ) ;
}
else if ( State = = EState : : SettingHeight
& & Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UDrawPolyPathProperties , ExtrudeHeight )
& & ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : Fixed
| | TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampFixed ) )
{
CurHeight = TransformProps - > ExtrudeHeight ;
UpdateExtrudePreview ( ) ;
}
else if ( Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UDrawPolyPathProperties , bRoundedCorners ) )
{
if ( ! TransformProps - > bRoundedCorners & & State = = EState : : SettingRadius )
{
GetToolManager ( ) - > EmitObjectChange ( this ,
MakeUnique < FDrawPolyPathStateChange > ( CurrentCurveTimestamp , EState : : SettingRadius ) ,
2022-02-04 20:17:09 -05:00
LOCTEXT ( " CancelRadiusTransactionName " , " Cancel Setting Corner Radius " ) ) ;
2022-01-10 15:56:01 -05:00
OnCompleteRadius ( ) ;
}
else if ( State ! = EState : : DrawingPath )
{
UpdatePathPreview ( ) ;
}
}
}
2020-01-27 20:11:15 -05:00
bool UDrawPolyPathTool : : HitTest ( const FRay & Ray , FHitResult & OutHit )
{
if ( SurfacePathMechanic ! = nullptr )
{
FFrame3d HitPoint ;
2021-11-17 21:06:46 -05:00
if ( SurfacePathMechanic - > IsHitByRay ( FRay3d ( Ray ) , HitPoint ) )
2020-01-27 20:11:15 -05:00
{
2021-11-17 21:06:46 -05:00
OutHit . Distance = FRay3d ( Ray ) . GetParameter ( HitPoint . Origin ) ;
2020-01-27 20:11:15 -05:00
OutHit . ImpactPoint = ( FVector ) HitPoint . Origin ;
OutHit . ImpactNormal = ( FVector ) HitPoint . Z ( ) ;
return true ;
}
return false ;
}
else if ( CurveDistMechanic ! = nullptr )
{
OutHit . ImpactPoint = Ray . PointAt ( 100 ) ;
OutHit . Distance = 100 ;
return true ;
}
else if ( ExtrudeHeightMechanic ! = nullptr )
{
OutHit . ImpactPoint = Ray . PointAt ( 100 ) ;
OutHit . Distance = 100 ;
return true ;
}
return false ;
}
2022-01-10 15:56:01 -05:00
void UDrawPolyPathTool : : OnUpdateModifierState ( int ModifierID , bool bIsOn )
{
if ( ModifierID = = ShiftModifierID & & bIgnoreSnappingToggle ! = bIsOn )
{
bIgnoreSnappingToggle = bIsOn ;
if ( SurfacePathMechanic )
{
SurfacePathMechanic - > bSnapToWorldGrid = ! bIgnoreSnappingToggle ;
}
}
}
2020-01-27 20:11:15 -05:00
FInputRayHit UDrawPolyPathTool : : IsHitByClick ( const FInputDeviceRay & ClickPos )
{
FHitResult OutHit ;
if ( HitTest ( ClickPos . WorldRay , OutHit ) )
{
return FInputRayHit ( OutHit . Distance ) ;
}
// background capture, if nothing else is hit
return FInputRayHit ( TNumericLimits < float > : : Max ( ) ) ;
}
void UDrawPolyPathTool : : OnClicked ( const FInputDeviceRay & ClickPos )
{
2022-01-10 15:56:01 -05:00
switch ( State )
2020-01-27 20:11:15 -05:00
{
2022-01-10 15:56:01 -05:00
case EState : : DrawingPath :
if ( SurfacePathMechanic ! = nullptr
& & SurfacePathMechanic - > TryAddPointFromRay ( ( FRay3d ) ClickPos . WorldRay ) )
2020-01-27 20:11:15 -05:00
{
2022-01-10 15:56:01 -05:00
if ( SurfacePathMechanic - > IsDone ( ) )
{
bPathIsClosed = SurfacePathMechanic - > LoopWasClosed ( ) ;
GetToolManager ( ) - > EmitObjectChange ( this ,
MakeUnique < FDrawPolyPathStateChange > ( CurrentCurveTimestamp , EState : : DrawingPath ) ,
2022-02-04 20:17:09 -05:00
LOCTEXT ( " FinishPathTransactionName " , " Finish Path " ) ) ;
2022-01-10 15:56:01 -05:00
OnCompleteSurfacePath ( ) ;
}
else
{
GetToolManager ( ) - > EmitObjectChange ( this ,
MakeUnique < FDrawPolyPathStateChange > ( CurrentCurveTimestamp , EState : : DrawingPath ) ,
2022-02-04 20:17:09 -05:00
LOCTEXT ( " AddToPathTransactionName " , " Add Point to Path " ) ) ;
2022-01-10 15:56:01 -05:00
}
}
break ;
case EState : : SettingWidth :
if ( TransformProps - > Width = = 0 )
{
// This can happen accidentally when the user has snapping turned on and it snaps to 0.
// We'll ignore that click and show an error message.
GetToolManager ( ) - > DisplayMessage (
2022-02-04 20:17:09 -05:00
LOCTEXT ( " ZeroWidthPathError " , " Cannot set path width to 0. " ) ,
2022-01-10 15:56:01 -05:00
EToolMessageLevel : : UserError ) ;
2020-01-27 20:11:15 -05:00
}
else
{
2022-01-10 15:56:01 -05:00
GetToolManager ( ) - > DisplayMessage ( FText ( ) , EToolMessageLevel : : UserError ) ;
GetToolManager ( ) - > EmitObjectChange ( this ,
MakeUnique < FDrawPolyPathStateChange > ( CurrentCurveTimestamp , EState : : SettingWidth ) ,
2022-02-04 20:17:09 -05:00
LOCTEXT ( " BeginWidthTransactionName " , " Set Path Width " ) ) ;
2022-01-10 15:56:01 -05:00
OnCompleteWidth ( ) ;
2020-01-27 20:11:15 -05:00
}
2022-01-10 15:56:01 -05:00
break ;
case EState : : SettingRadius :
GetToolManager ( ) - > EmitObjectChange ( this ,
MakeUnique < FDrawPolyPathStateChange > ( CurrentCurveTimestamp , EState : : SettingRadius ) ,
2022-02-04 20:17:09 -05:00
LOCTEXT ( " BeginRadiusTransactionName " , " Set Corner Radius " ) ) ;
2022-01-10 15:56:01 -05:00
OnCompleteRadius ( ) ;
break ;
case EState : : SettingHeight :
if ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : Interactive
| | TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampInteractive )
{
CurHeight = TransformProps - > ExtrudeHeight ;
}
OnCompleteExtrudeHeight ( ) ;
break ;
2020-01-27 20:11:15 -05:00
}
}
FInputRayHit UDrawPolyPathTool : : BeginHoverSequenceHitTest ( const FInputDeviceRay & PressPos )
{
FHitResult OutHit ;
if ( HitTest ( PressPos . WorldRay , OutHit ) )
{
return FInputRayHit ( OutHit . Distance ) ;
}
// background capture, if nothing else is hit
return FInputRayHit ( TNumericLimits < float > : : Max ( ) ) ;
}
bool UDrawPolyPathTool : : OnUpdateHover ( const FInputDeviceRay & DevicePos )
{
2022-01-10 15:56:01 -05:00
switch ( State )
2020-01-27 20:11:15 -05:00
{
2022-01-10 15:56:01 -05:00
case EState : : DrawingPath :
if ( ensure ( SurfacePathMechanic ! = nullptr ) )
2021-11-07 23:43:01 -05:00
{
2022-01-10 15:56:01 -05:00
SurfacePathMechanic - > UpdatePreviewPoint ( ( FRay3d ) DevicePos . WorldRay ) ;
2021-12-10 18:05:44 -05:00
}
2022-01-10 15:56:01 -05:00
break ;
case EState : : SettingWidth :
if ( CurveDistMechanic ! = nullptr )
2021-12-10 18:05:44 -05:00
{
2022-01-10 15:56:01 -05:00
CurveDistMechanic - > UpdateCurrentDistance ( DevicePos . WorldRay ) ;
if ( TransformProps - > WidthMode = = EDrawPolyPathWidthMode : : Interactive )
{
double CurveDistance = CurveDistMechanic - > CurrentDistance ;
if ( ! bIgnoreSnappingToggle )
{
CurveDistance = ToolSceneQueriesUtil : : SnapDistanceToWorldGridSize ( this , CurveDistance ) ;
}
TransformProps - > Width = CurveDistance * 2 ;
UpdatePathPreview ( ) ;
}
2021-11-07 23:43:01 -05:00
}
2022-01-10 15:56:01 -05:00
break ;
case EState : : SettingRadius :
if ( CurveDistMechanic ! = nullptr )
2021-11-07 23:43:01 -05:00
{
2022-01-10 15:56:01 -05:00
CurveDistMechanic - > UpdateCurrentDistance ( DevicePos . WorldRay ) ;
if ( TransformProps - > RadiusMode = = EDrawPolyPathRadiusMode : : Interactive )
{
double CurveDistance = CurveDistMechanic - > CurrentDistance ;
if ( ! bIgnoreSnappingToggle )
{
CurveDistance = ToolSceneQueriesUtil : : SnapDistanceToWorldGridSize ( this , CurveDistance ) ;
}
TransformProps - > CornerRadius = FMath : : Clamp ( CurveDistance / TransformProps - > Width , 0.0 , 2.0 ) ;
UpdatePathPreview ( ) ;
}
2021-11-07 23:43:01 -05:00
}
2022-01-10 15:56:01 -05:00
break ;
case EState : : SettingHeight :
if ( ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : Interactive
| | TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampInteractive )
& & ensure ( ExtrudeHeightMechanic ! = nullptr ) )
{
ExtrudeHeightMechanic - > UpdateCurrentDistance ( DevicePos . WorldRay ) ;
CurHeight = ExtrudeHeightMechanic - > CurrentHeight ;
TransformProps - > ExtrudeHeight = ExtrudeHeightMechanic - > CurrentHeight ;
UpdateExtrudePreview ( ) ;
}
break ;
2020-01-27 20:11:15 -05:00
}
2022-01-10 15:56:01 -05:00
2020-01-27 20:11:15 -05:00
return true ;
}
2020-04-18 18:42:59 -04:00
void UDrawPolyPathTool : : OnTick ( float DeltaTime )
2020-01-27 20:11:15 -05:00
{
if ( PlaneMechanic ! = nullptr )
{
PlaneMechanic - > Tick ( DeltaTime ) ;
}
}
void UDrawPolyPathTool : : Render ( IToolsContextRenderAPI * RenderAPI )
{
GetToolManager ( ) - > GetContextQueriesAPI ( ) - > GetCurrentViewState ( CameraState ) ;
if ( PlaneMechanic ! = nullptr )
{
PlaneMechanic - > Render ( RenderAPI ) ;
}
if ( ExtrudeHeightMechanic ! = nullptr )
{
ExtrudeHeightMechanic - > Render ( RenderAPI ) ;
}
if ( CurveDistMechanic ! = nullptr )
{
CurveDistMechanic - > Render ( RenderAPI ) ;
}
if ( SurfacePathMechanic ! = nullptr )
{
SurfacePathMechanic - > Render ( RenderAPI ) ;
}
2021-10-13 12:32:03 -04:00
if ( CurPolyLoop . Num ( ) > 0 )
{
FToolDataVisualizer LineRenderer ;
LineRenderer . LineColor = LinearColors : : DarkOrange3f ( ) ;
LineRenderer . LineThickness = 4.0f ;
LineRenderer . bDepthTested = false ;
LineRenderer . BeginFrame ( RenderAPI ) ;
int32 NumPoints = CurPolyLoop . Num ( ) ;
for ( int32 k = 0 ; k < NumPoints ; + + k )
{
LineRenderer . DrawLine ( CurPolyLoop [ k ] , CurPolyLoop [ ( k + 1 ) % NumPoints ] ) ;
}
if ( SecondPolyLoop . Num ( ) > 0 )
{
NumPoints = SecondPolyLoop . Num ( ) ;
for ( int32 k = 0 ; k < NumPoints ; + + k )
{
LineRenderer . DrawLine ( SecondPolyLoop [ k ] , SecondPolyLoop [ ( k + 1 ) % NumPoints ] ) ;
}
}
LineRenderer . EndFrame ( ) ;
}
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : InitializeNewSurfacePath ( )
{
2022-01-10 15:56:01 -05:00
State = EState : : DrawingPath ;
CurveDistMechanic = nullptr ;
CurveDistMechanic = nullptr ;
2020-01-27 20:11:15 -05:00
SurfacePathMechanic = NewObject < UCollectSurfacePathMechanic > ( this ) ;
SurfacePathMechanic - > Setup ( this ) ;
double SnapTol = ToolSceneQueriesUtil : : GetDefaultVisualAngleSnapThreshD ( ) ;
SurfacePathMechanic - > SpatialSnapPointsFunc = [ this , SnapTol ] ( FVector3d Position1 , FVector3d Position2 )
{
2021-10-21 17:57:49 -04:00
return ToolSceneQueriesUtil : : PointSnapQuery ( this - > CameraState , Position1 , Position2 , SnapTol ) ;
2020-01-27 20:11:15 -05:00
} ;
2020-03-09 13:52:38 -04:00
SurfacePathMechanic - > SetDoubleClickOrCloseLoopMode ( ) ;
2022-01-10 15:56:01 -05:00
SurfacePathMechanic - > bSnapToWorldGrid = ! bIgnoreSnappingToggle ;
2021-10-25 20:05:28 -04:00
2020-01-27 20:11:15 -05:00
UpdateSurfacePathPlane ( ) ;
2020-03-10 14:00:36 -04:00
ShowStartupMessage ( ) ;
2020-01-27 20:11:15 -05:00
}
bool UDrawPolyPathTool : : CanUpdateDrawPlane ( ) const
{
2022-01-10 15:56:01 -05:00
return ( State = = EState : : DrawingPath & & SurfacePathMechanic ! = nullptr & & SurfacePathMechanic - > HitPath . Num ( ) = = 0 ) ;
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : UpdateSurfacePathPlane ( )
{
if ( SurfacePathMechanic ! = nullptr )
{
SurfacePathMechanic - > InitializePlaneSurface ( DrawPlaneWorld ) ;
}
}
void UDrawPolyPathTool : : OnCompleteSurfacePath ( )
{
check ( SurfacePathMechanic ! = nullptr ) ;
CurPathPoints = SurfacePathMechanic - > HitPath ;
int NumPoints = CurPathPoints . Num ( ) ;
// align frames
FVector3d PlaneNormal = DrawPlaneWorld . Z ( ) ;
2021-03-17 19:32:44 -04:00
CurPathPoints [ 0 ] . ConstrainedAlignAxis ( 0 , UE : : Geometry : : Normalized ( CurPathPoints [ 1 ] . Origin - CurPathPoints [ 0 ] . Origin ) , PlaneNormal ) ;
CurPathPoints [ NumPoints - 1 ] . ConstrainedAlignAxis ( 0 , UE : : Geometry : : Normalized ( CurPathPoints [ NumPoints - 1 ] . Origin - CurPathPoints [ NumPoints - 2 ] . Origin ) , PlaneNormal ) ;
2020-01-27 20:11:15 -05:00
double DistOffsetDelta = 0.01 ;
OffsetScaleFactors . SetNum ( NumPoints ) ;
OffsetScaleFactors [ 0 ] = OffsetScaleFactors [ NumPoints - 1 ] = 1.0 ;
2020-03-09 13:52:38 -04:00
// Set local frames for path points. If the path is closed, we will adjust the first and last frames for continuity,
// otherwise we will leave them as set above.
int LastPointIndex = bPathIsClosed ? NumPoints : NumPoints - 1 ;
int FirstPointIndex = bPathIsClosed ? 0 : 1 ;
for ( int j = FirstPointIndex ; j < LastPointIndex ; + + j )
2020-01-27 20:11:15 -05:00
{
2020-03-09 13:52:38 -04:00
int NextJ = ( j + 1 ) % NumPoints ;
int PrevJ = ( j - 1 + NumPoints ) % NumPoints ;
FVector3d Prev ( CurPathPoints [ PrevJ ] . Origin ) , Next ( CurPathPoints [ NextJ ] . Origin ) , Cur ( CurPathPoints [ j ] . Origin ) ;
2020-01-27 20:11:15 -05:00
FLine3d Line1 ( FLine3d : : FromPoints ( Prev , Cur ) ) , Line2 ( FLine3d : : FromPoints ( Cur , Next ) ) ;
Line1 . Origin + = DistOffsetDelta * PlaneNormal . Cross ( Line1 . Direction ) ;
Line2 . Origin + = DistOffsetDelta * PlaneNormal . Cross ( Line2 . Direction ) ;
2021-12-10 18:05:44 -05:00
if ( FMath : : Abs ( Line1 . Direction . Dot ( Line2 . Direction ) ) > 0.999 )
2020-01-27 20:11:15 -05:00
{
2021-03-17 19:32:44 -04:00
CurPathPoints [ j ] . ConstrainedAlignAxis ( 0 , UE : : Geometry : : Normalized ( Next - Prev ) , PlaneNormal ) ;
2020-01-27 20:11:15 -05:00
OffsetScaleFactors [ j ] = 1.0 ;
}
else
{
2021-03-30 21:25:22 -04:00
FDistLine3Line3d LineDist ( Line1 , Line2 ) ;
LineDist . GetSquared ( ) ;
FVector3d OffsetPoint = 0.5 * ( LineDist . Line1ClosestPoint + LineDist . Line2ClosestPoint ) ;
OffsetScaleFactors [ j ] = Distance ( OffsetPoint , Cur ) / DistOffsetDelta ;
2021-03-17 19:32:44 -04:00
FVector3d TangentDir = UE : : Geometry : : Normalized ( OffsetPoint - Cur ) . Cross ( PlaneNormal ) ;
2020-01-27 20:11:15 -05:00
CurPathPoints [ j ] . ConstrainedAlignAxis ( 0 , TangentDir , PlaneNormal ) ;
}
}
CurPolyLine . Reset ( ) ;
for ( const FFrame3d & Point : SurfacePathMechanic - > HitPath )
{
CurPolyLine . Add ( Point . Origin ) ;
}
SurfacePathMechanic = nullptr ;
2022-01-10 15:56:01 -05:00
InitializePreviewMesh ( ) ;
// Progress to next state
BeginSettingWidth ( ) ;
2020-01-27 20:11:15 -05:00
}
2022-01-10 15:56:01 -05:00
void UDrawPolyPathTool : : BeginSettingWidth ( )
2020-01-27 20:11:15 -05:00
{
2022-01-10 15:56:01 -05:00
// Note that even when the width is constant, we still wait for a click from the user because we
// want them to have a chance to edit it in the detail panel, or to switch to interactive width mode.
2021-11-23 10:39:20 -05:00
2022-01-10 15:56:01 -05:00
State = EState : : SettingWidth ;
if ( TransformProps - > WidthMode = = EDrawPolyPathWidthMode : : Interactive )
{
bHasSavedWidth = true ;
SavedWidth = TransformProps - > Width ;
}
else if ( bHasSavedWidth )
{
TransformProps - > Width = SavedWidth ;
bHasSavedWidth = false ;
}
2020-01-27 20:11:15 -05:00
CurveDistMechanic = NewObject < USpatialCurveDistanceMechanic > ( this ) ;
CurveDistMechanic - > Setup ( this ) ;
2022-01-29 14:37:53 -05:00
CurveDistMechanic - > InitializePolyCurve ( CurPolyLine , FTransform3d : : Identity ) ;
2020-01-27 20:11:15 -05:00
2022-01-10 15:56:01 -05:00
ExtrudeHeightMechanic = nullptr ;
2020-03-10 14:00:36 -04:00
2020-01-27 20:11:15 -05:00
UpdatePathPreview ( ) ;
2022-01-10 15:56:01 -05:00
if ( TransformProps - > WidthMode = = EDrawPolyPathWidthMode : : Interactive )
{
GetToolManager ( ) - > DisplayMessage (
2022-02-04 20:17:09 -05:00
LOCTEXT ( " InteractiveSetWidthInstructions " , " Set the width of the path by clicking on the drawing plane. Hold Shift to ignore snapping. " ) ,
2022-01-10 15:56:01 -05:00
EToolMessageLevel : : UserNotification ) ;
}
else
{
GetToolManager ( ) - > DisplayMessage (
2022-02-04 20:17:09 -05:00
LOCTEXT ( " FixedSetWidthInstructions " , " Click in viewport to accept fixed path width, or change it in details panel. " ) ,
2022-01-10 15:56:01 -05:00
EToolMessageLevel : : UserNotification ) ;
}
2021-12-10 18:05:44 -05:00
}
void UDrawPolyPathTool : : OnCompleteWidth ( )
{
if ( TransformProps - > bRoundedCorners )
{
2022-01-10 15:56:01 -05:00
BeginSettingRadius ( ) ;
2021-12-10 18:05:44 -05:00
}
else
{
2022-01-10 15:56:01 -05:00
// Skip radius setting
OnCompleteRadius ( ) ;
2021-12-10 18:05:44 -05:00
}
2020-01-27 20:11:15 -05:00
}
2022-01-10 15:56:01 -05:00
void UDrawPolyPathTool : : BeginSettingRadius ( )
2021-12-10 18:05:44 -05:00
{
2022-01-10 15:56:01 -05:00
// Note that even when the radius is constant, we still wait for a click from the user because we
// want them to have a chance to edit it in the detail panel, or to switch to interactive radius mode.
2020-01-27 20:11:15 -05:00
2022-01-10 15:56:01 -05:00
State = EState : : SettingRadius ;
if ( TransformProps - > RadiusMode = = EDrawPolyPathRadiusMode : : Interactive )
{
bHasSavedRadius = true ;
SavedRadius = TransformProps - > CornerRadius ;
}
else if ( bHasSavedRadius )
{
TransformProps - > CornerRadius = SavedRadius ;
bHasSavedRadius = false ;
}
2021-12-10 18:05:44 -05:00
CurveDistMechanic = NewObject < USpatialCurveDistanceMechanic > ( this ) ;
CurveDistMechanic - > Setup ( this ) ;
2022-01-29 14:37:53 -05:00
CurveDistMechanic - > InitializePolyCurve ( CurPolyLine , FTransform3d : : Identity ) ;
2021-12-10 18:05:44 -05:00
2022-01-10 15:56:01 -05:00
ExtrudeHeightMechanic = nullptr ;
UpdatePathPreview ( ) ;
if ( TransformProps - > RadiusMode = = EDrawPolyPathRadiusMode : : Interactive )
{
GetToolManager ( ) - > DisplayMessage (
2022-02-04 20:17:09 -05:00
LOCTEXT ( " InteractiveSetRadiusInstructions " , " Set the radius of the corners by clicking on the drawing plane. Hold Shift to ignore snapping. " ) ,
2022-01-10 15:56:01 -05:00
EToolMessageLevel : : UserNotification ) ;
}
else
{
GetToolManager ( ) - > DisplayMessage (
2022-02-04 20:17:09 -05:00
LOCTEXT ( " FixedSetRadiusInstructions " , " Click in viewport to accept fixed corner radius, or change it in details panel. " ) ,
2022-01-10 15:56:01 -05:00
EToolMessageLevel : : UserNotification ) ;
}
2021-12-10 18:05:44 -05:00
}
void UDrawPolyPathTool : : OnCompleteRadius ( )
2020-01-27 20:11:15 -05:00
{
2022-01-10 15:56:01 -05:00
BeginSettingHeight ( ) ;
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : OnCompleteExtrudeHeight ( )
{
ExtrudeHeightMechanic = nullptr ;
ClearPreview ( ) ;
2021-11-23 10:39:20 -05:00
EmitNewObject ( ) ;
2020-01-27 20:11:15 -05:00
InitializeNewSurfacePath ( ) ;
CurrentCurveTimestamp + + ;
}
void UDrawPolyPathTool : : UpdatePathPreview ( )
{
2022-01-10 15:56:01 -05:00
if ( EditPreview = = nullptr )
{
return ;
}
2020-01-27 20:11:15 -05:00
FDynamicMesh3 PathMesh ;
2022-09-09 01:37:45 -04:00
FVector3d MeshCenter = GeneratePathMesh ( PathMesh ) ;
EditPreview - > SetTransform ( FTransform3d ( MeshCenter ) ) ;
2020-01-27 20:11:15 -05:00
2022-01-10 15:56:01 -05:00
if ( State = = EState : : SettingHeight )
{
EditPreview - > InitializeExtrudeType ( MoveTemp ( PathMesh ) , DrawPlaneWorld . Z ( ) , nullptr , false ) ;
UpdateExtrudePreview ( ) ;
}
else
{
EditPreview - > ReplaceMesh ( MoveTempIfPossible ( PathMesh ) ) ;
}
}
2020-01-27 20:11:15 -05:00
2022-09-09 01:37:45 -04:00
FVector3d UDrawPolyPathTool : : GeneratePathMesh ( FDynamicMesh3 & Mesh )
2020-01-27 20:11:15 -05:00
{
2021-10-13 12:32:03 -04:00
CurPolyLoop . Reset ( ) ;
SecondPolyLoop . Reset ( ) ;
2021-11-18 14:37:34 -05:00
const bool bRampMode = ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampFixed ) | | ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampInteractive ) ;
2021-12-10 18:05:44 -05:00
constexpr bool bLimitCornerRadius = true ;
2022-09-09 01:37:45 -04:00
FVector3d MeshCenter = DrawPolyPathToolLocals : : GeneratePathMesh ( Mesh ,
2021-12-10 18:05:44 -05:00
CurPathPoints ,
OffsetScaleFactors ,
2022-01-10 15:56:01 -05:00
TransformProps - > Width / 2 ,
2021-12-10 18:05:44 -05:00
bPathIsClosed ,
bRampMode ,
TransformProps - > bSinglePolyGroup ,
2022-01-10 15:56:01 -05:00
// Treat radius 0 corners as not rounded, rather than placing a bunch of vertices
// in the same place.
TransformProps - > bRoundedCorners & & TransformProps - > CornerRadius > 0 ,
TransformProps - > CornerRadius ,
2021-12-10 18:05:44 -05:00
bLimitCornerRadius ,
TransformProps - > RadialSlices ) ;
2021-11-18 14:37:34 -05:00
FMeshNormals : : QuickRecomputeOverlayNormals ( Mesh ) ;
FMeshBoundaryLoops Loops ( & Mesh , true ) ;
if ( Loops . Loops . Num ( ) > 0 )
2020-01-27 20:11:15 -05:00
{
2021-11-18 14:37:34 -05:00
Loops . Loops [ 0 ] . GetVertices < FVector3d > ( CurPolyLoop ) ;
2022-09-19 21:58:18 -04:00
for ( FVector3d & Pt : CurPolyLoop )
{
Pt + = MeshCenter ;
}
2021-11-18 14:37:34 -05:00
if ( Loops . Loops . Num ( ) > 1 )
2021-11-07 23:43:01 -05:00
{
2021-11-18 14:37:34 -05:00
Loops . Loops [ 1 ] . GetVertices < FVector3d > ( SecondPolyLoop ) ;
2022-09-19 21:58:18 -04:00
for ( FVector3d & Pt : SecondPolyLoop )
{
Pt + = MeshCenter ;
}
2021-10-13 12:32:03 -04:00
}
2020-01-27 20:11:15 -05:00
}
2022-09-09 01:37:45 -04:00
return MeshCenter ;
2020-01-27 20:11:15 -05:00
}
2022-01-10 15:56:01 -05:00
void UDrawPolyPathTool : : BeginSettingHeight ( )
{
State = EState : : SettingHeight ;
2020-01-27 20:11:15 -05:00
2022-01-10 15:56:01 -05:00
if ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : Interactive
| | TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampInteractive )
{
BeginInteractiveExtrudeHeight ( ) ;
}
else
{
BeginConstantExtrudeHeight ( ) ;
}
}
2020-01-27 20:11:15 -05:00
void UDrawPolyPathTool : : BeginInteractiveExtrudeHeight ( )
{
2021-11-23 10:39:20 -05:00
bHasSavedExtrudeHeight = true ;
SavedExtrudeHeight = TransformProps - > ExtrudeHeight ;
2020-01-27 20:11:15 -05:00
// begin extrude
ExtrudeHeightMechanic = NewObject < UPlaneDistanceFromHitMechanic > ( this ) ;
ExtrudeHeightMechanic - > Setup ( this ) ;
ExtrudeHeightMechanic - > WorldHitQueryFunc = [ this ] ( const FRay & WorldRay , FHitResult & HitResult )
{
2021-12-09 14:46:09 -05:00
return ToolSceneQueriesUtil : : FindNearestVisibleObjectHit ( this , HitResult , WorldRay ) ;
2020-01-27 20:11:15 -05:00
} ;
ExtrudeHeightMechanic - > WorldPointSnapFunc = [ this ] ( const FVector3d & WorldPos , FVector3d & SnapPos )
{
2022-01-10 15:56:01 -05:00
return ! bIgnoreSnappingToggle & & ToolSceneQueriesUtil : : FindWorldGridSnapPoint ( this , WorldPos , SnapPos ) ;
2020-01-27 20:11:15 -05:00
} ;
ExtrudeHeightMechanic - > CurrentHeight = 1.0f ; // initialize to something non-zero...prob should be based on polygon bounds maybe?
FDynamicMesh3 PathMesh ;
2022-09-09 01:37:45 -04:00
FVector3d MeshCenter = GeneratePathMesh ( PathMesh ) ;
EditPreview - > SetTransform ( FTransform ( MeshCenter ) ) ;
2020-01-27 20:11:15 -05:00
EditPreview - > InitializeExtrudeType ( MoveTemp ( PathMesh ) , DrawPlaneWorld . Z ( ) , nullptr , false ) ;
FDynamicMesh3 TmpMesh ;
EditPreview - > MakeExtrudeTypeHitTargetMesh ( TmpMesh , false ) ;
2022-01-10 15:56:01 -05:00
2020-01-27 20:11:15 -05:00
FFrame3d UseFrame = DrawPlaneWorld ;
2022-09-09 01:37:45 -04:00
UseFrame . Origin = MeshCenter ;
FTransform3d MeshToFrame ( FQuat ( UseFrame . Rotation . Inverse ( ) ) ) ;
ExtrudeHeightMechanic - > Initialize ( MoveTemp ( TmpMesh ) , UseFrame , MeshToFrame ) ;
2020-03-10 14:00:36 -04:00
ShowExtrudeMessage ( ) ;
2020-01-27 20:11:15 -05:00
}
2022-01-10 15:56:01 -05:00
void UDrawPolyPathTool : : BeginConstantExtrudeHeight ( )
{
State = EState : : SettingHeight ;
2020-01-27 20:11:15 -05:00
2022-01-10 15:56:01 -05:00
if ( bHasSavedExtrudeHeight )
{
TransformProps - > ExtrudeHeight = SavedExtrudeHeight ;
bHasSavedExtrudeHeight = false ;
}
if ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : Flat )
{
CurHeight = 0.0 ;
}
else if ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : Fixed | | TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampFixed )
{
CurHeight = TransformProps - > ExtrudeHeight ;
}
else
{
ensure ( false ) ;
}
FDynamicMesh3 PathMesh ;
2022-09-09 01:37:45 -04:00
FVector3d Center = GeneratePathMesh ( PathMesh ) ;
EditPreview - > SetTransform ( FTransform ( Center ) ) ;
2022-01-10 15:56:01 -05:00
EditPreview - > InitializeExtrudeType ( MoveTemp ( PathMesh ) , DrawPlaneWorld . Z ( ) , nullptr , false ) ;
UpdateExtrudePreview ( ) ;
ExtrudeHeightMechanic = nullptr ;
GetToolManager ( ) - > DisplayMessage (
2022-02-04 20:17:09 -05:00
LOCTEXT ( " FixedSetHeightInstructions " , " Click in viewport to accept fixed path height, or change it in details panel. " ) ,
2022-01-10 15:56:01 -05:00
EToolMessageLevel : : UserNotification ) ;
}
// This should only be used after doing EditPreview->InitializeExtrudeType, when setting height
2020-01-27 20:11:15 -05:00
void UDrawPolyPathTool : : UpdateExtrudePreview ( )
{
2021-11-18 14:37:34 -05:00
EditPreview - > UpdateExtrudeType ( [ & ] ( FDynamicMesh3 & Mesh ) { GenerateExtrudeMesh ( Mesh ) ; } , true ) ;
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : InitializePreviewMesh ( )
{
if ( EditPreview = = nullptr )
{
EditPreview = NewObject < UPolyEditPreviewMesh > ( this ) ;
EditPreview - > CreateInWorld ( TargetWorld , FTransform : : Identity ) ;
2021-10-07 22:25:54 -04:00
ToolSetupUtil : : ApplyRenderingConfigurationToPreview ( EditPreview , nullptr ) ;
2020-01-27 20:11:15 -05:00
if ( MaterialProperties - > Material = = nullptr )
{
EditPreview - > SetMaterial (
ToolSetupUtil : : GetSelectionMaterial ( FLinearColor ( 0.8f , 0.75f , 0.0f ) , GetToolManager ( ) ) ) ;
}
else
{
2020-10-09 22:42:26 -04:00
EditPreview - > SetMaterial ( MaterialProperties - > Material . Get ( ) ) ;
2020-01-27 20:11:15 -05:00
}
}
}
void UDrawPolyPathTool : : ClearPreview ( )
{
if ( EditPreview ! = nullptr )
{
EditPreview - > Disconnect ( ) ;
EditPreview = nullptr ;
}
2021-10-13 12:32:03 -04:00
CurPolyLoop . Reset ( ) ;
SecondPolyLoop . Reset ( ) ;
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : GenerateExtrudeMesh ( FDynamicMesh3 & PathMesh )
{
2022-01-10 15:56:01 -05:00
if ( CurHeight = = 0 )
{
// This behavior should match whatever we do in DrawPolygonTool. Currently a flat mesh should
// just be a one-sided strip, not a degenerately thin closed shape.
return ;
}
2020-01-27 20:11:15 -05:00
FExtrudeMesh Extruder ( & PathMesh ) ;
2021-11-18 14:37:34 -05:00
const FVector3d ExtrudeDir = DrawPlaneWorld . Z ( ) ;
const bool bRampMode = ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampFixed ) | | ( TransformProps - > ExtrudeMode = = EDrawPolyPathExtrudeMode : : RampInteractive ) ;
if ( bRampMode )
{
const double RampStartRatio = TransformProps - > RampStartRatio ;
const double StartHeight = FMathd : : Max ( 0.1 , RampStartRatio * FMathd : : Abs ( CurHeight ) ) * FMathd : : Sign ( CurHeight ) ;
const double EndHeight = CurHeight ;
Extruder . ExtrudedPositionFunc = [ & PathMesh , StartHeight , EndHeight , & ExtrudeDir ] ( const FVector3d & P , const FVector3f & N , int32 VID ) {
FVector2f UV = PathMesh . GetVertexUV ( VID ) ;
double UseHeight = FMathd : : Lerp ( StartHeight , EndHeight , UV . X ) ;
return P + UseHeight * ExtrudeDir ;
} ;
}
else
{
Extruder . ExtrudedPositionFunc = [ this , & ExtrudeDir ] ( const FVector3d & P , const FVector3f & N , int32 VID ) {
return P + CurHeight * ExtrudeDir ;
} ;
}
const FAxisAlignedBox3d Bounds = PathMesh . GetBounds ( ) ;
2020-01-27 20:11:15 -05:00
Extruder . UVScaleFactor = 1.0 / Bounds . MaxDim ( ) ;
Extruder . IsPositiveOffset = ( CurHeight > = 0 ) ;
Extruder . Apply ( ) ;
2020-03-03 16:57:38 -05:00
FMeshNormals : : QuickRecomputeOverlayNormals ( PathMesh ) ;
2020-01-27 20:11:15 -05:00
}
2021-11-23 10:39:20 -05:00
void UDrawPolyPathTool : : EmitNewObject ( )
2020-01-27 20:11:15 -05:00
{
FDynamicMesh3 PathMesh ;
2022-09-09 01:37:45 -04:00
FVector3d MeshCenter = GeneratePathMesh ( PathMesh ) ;
2021-11-18 14:37:34 -05:00
GenerateExtrudeMesh ( PathMesh ) ;
2020-01-27 20:11:15 -05:00
PathMesh . DiscardVertexUVs ( ) ; // throw away arc lengths
2022-09-09 01:37:45 -04:00
FFrame3d MeshTransform = DrawPlaneWorld ; // The desired frame for the final output mesh
FVector3d WorldCenter = PathMesh . GetBounds ( ) . Center ( ) + MeshCenter ;
MeshTransform . Origin = MeshTransform . ToPlane ( WorldCenter , 2 ) ;
// Transform the mesh from its MeshCenter-offset space to the MeshTransform frame space
// The below is equivalent to applying: MeshTransform.Rotation^-1 (Mesh + MeshCenter - MeshTransform.Origin)
FFrame3d LocalCenterMeshTransform = MeshTransform ;
LocalCenterMeshTransform . Origin - = MeshCenter ;
MeshTransforms : : WorldToFrameCoords ( PathMesh , LocalCenterMeshTransform ) ;
2022-08-25 11:51:02 -04:00
UE : : Geometry : : FMeshTangentsf : : ComputeDefaultOverlayTangents ( PathMesh ) ;
2020-01-27 20:11:15 -05:00
2022-02-04 20:17:09 -05:00
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " CreatePolyPathTransactionName " , " Create PolyPath " ) ) ;
2020-01-27 20:11:15 -05:00
2021-06-02 15:58:00 -04:00
FCreateMeshObjectParams NewMeshObjectParams ;
NewMeshObjectParams . TargetWorld = TargetWorld ;
NewMeshObjectParams . Transform = MeshTransform . ToFTransform ( ) ;
NewMeshObjectParams . BaseName = TEXT ( " Path " ) ;
NewMeshObjectParams . Materials . Add ( MaterialProperties - > Material . Get ( ) ) ;
NewMeshObjectParams . SetMesh ( & PathMesh ) ;
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04: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-01-27 20:11:15 -05:00
{
2021-06-02 15:58:00 -04:00
ToolSelectionUtil : : SetNewActorSelection ( GetToolManager ( ) , Result . NewActor ) ;
2020-01-27 20:11:15 -05:00
}
GetToolManager ( ) - > EndUndoTransaction ( ) ;
2021-10-13 12:32:03 -04:00
2021-11-23 10:39:20 -05:00
if ( bHasSavedWidth )
{
TransformProps - > Width = SavedWidth ;
bHasSavedWidth = false ;
}
2022-01-10 15:56:01 -05:00
if ( bHasSavedRadius )
{
TransformProps - > CornerRadius = SavedRadius ;
bHasSavedRadius = false ;
}
2021-11-23 10:39:20 -05:00
if ( bHasSavedExtrudeHeight )
{
TransformProps - > ExtrudeHeight = SavedExtrudeHeight ;
bHasSavedExtrudeHeight = false ;
}
2021-10-13 12:32:03 -04:00
CurPolyLoop . Reset ( ) ;
SecondPolyLoop . Reset ( ) ;
2020-01-27 20:11:15 -05:00
}
2020-03-10 14:00:36 -04:00
void UDrawPolyPathTool : : ShowStartupMessage ( )
{
GetToolManager ( ) - > DisplayMessage (
2022-02-04 20:17:09 -05:00
LOCTEXT ( " StartDrawInstructions " , " Draw a path on the drawing plane, set its width, and extrude it. Left-click to place path vertices, and click on the last or first vertex to complete the path. Hold Shift to ignore snapping while drawing. " ) ,
2020-03-10 14:00:36 -04:00
EToolMessageLevel : : UserNotification ) ;
}
void UDrawPolyPathTool : : ShowExtrudeMessage ( )
{
GetToolManager ( ) - > DisplayMessage (
2022-02-04 20:17:09 -05:00
LOCTEXT ( " InteractiveSetHeightInstructions " , " Set the height of the extrusion by positioning the mouse over the extrusion volume, or over objects to snap to their heights. Hold Shift to ignore snapping. " ) ,
2020-03-10 14:00:36 -04:00
EToolMessageLevel : : UserNotification ) ;
}
2022-01-10 15:56:01 -05:00
void UDrawPolyPathTool : : UndoCurrentOperation ( EState DestinationState )
2020-01-27 20:11:15 -05:00
{
2022-01-10 15:56:01 -05:00
switch ( State )
2020-01-27 20:11:15 -05:00
{
2022-01-10 15:56:01 -05:00
case EState : : DrawingPath :
if ( ensure ( DestinationState = = EState : : DrawingPath ) )
2020-01-27 20:11:15 -05:00
{
2022-01-10 15:56:01 -05:00
SurfacePathMechanic - > PopLastPoint ( ) ;
if ( SurfacePathMechanic - > HitPath . Num ( ) = = 0 )
{
CurrentCurveTimestamp + + ;
}
2020-01-27 20:11:15 -05:00
}
2022-01-10 15:56:01 -05:00
break ;
case EState : : SettingWidth :
if ( ensure ( DestinationState = = EState : : DrawingPath ) )
2021-12-10 18:05:44 -05:00
{
CurveDistMechanic = nullptr ;
ClearPreview ( ) ;
InitializeNewSurfacePath ( ) ;
SurfacePathMechanic - > HitPath = CurPathPoints ;
}
2022-01-10 15:56:01 -05:00
break ;
case EState : : SettingRadius :
if ( ensure ( DestinationState = = EState : : SettingWidth ) )
2021-12-10 18:05:44 -05:00
{
2022-01-10 15:56:01 -05:00
BeginSettingWidth ( ) ;
2021-12-10 18:05:44 -05:00
}
2022-01-10 15:56:01 -05:00
break ;
case EState : : SettingHeight :
if ( DestinationState = = EState : : SettingRadius )
2021-12-10 18:05:44 -05:00
{
2022-01-10 15:56:01 -05:00
TransformProps - > bRoundedCorners = true ;
BeginSettingRadius ( ) ;
2021-12-10 18:05:44 -05:00
}
2022-01-10 15:56:01 -05:00
else if ( ensure ( DestinationState = = EState : : SettingWidth ) )
2021-12-10 18:05:44 -05:00
{
2022-01-10 15:56:01 -05:00
BeginSettingWidth ( ) ;
2021-12-10 18:05:44 -05:00
}
2022-01-10 15:56:01 -05:00
break ;
2020-01-27 20:11:15 -05:00
}
}
void FDrawPolyPathStateChange : : Revert ( UObject * Object )
{
2022-01-10 15:56:01 -05:00
Cast < UDrawPolyPathTool > ( Object ) - > UndoCurrentOperation ( PreviousState ) ;
2020-01-27 20:11:15 -05:00
bHaveDoneUndo = true ;
}
bool FDrawPolyPathStateChange : : HasExpired ( UObject * Object ) const
{
return bHaveDoneUndo | | ( Cast < UDrawPolyPathTool > ( Object ) - > CheckInCurve ( CurveTimestamp ) = = false ) ;
}
FString FDrawPolyPathStateChange : : ToString ( ) const
{
return TEXT ( " FDrawPolyPathStateChange " ) ;
}
# undef LOCTEXT_NAMESPACE
2022-09-28 01:06:15 -04:00