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 "Intersection/IntersectionUtil.h"
# include "Util/ColorConstants.h"
# include "ToolSetupUtil.h"
2021-05-22 01:32:46 -04:00
# include "DynamicMesh/MeshIndexUtil.h"
2020-01-27 20:11:15 -05:00
# include "Generators/RectangleMeshGenerator.h"
# include "Distance/DistLine3Line3.h"
2021-06-02 15:58:00 -04:00
# include "ModelingObjectsCreationAPI.h"
2021-06-13 00:35:22 -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:35:22 -04:00
# include "DynamicMesh/MeshNormals.h"
2020-01-27 20:11:15 -05:00
2021-03-09 19:33:56 -04:00
# include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types)
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
2020-03-09 13:52:38 -04:00
namespace
{
// Simple mesh generator that generates a quad for each edge of an input polygon
class FPolygonEdgeMeshGenerator : public FMeshShapeGenerator
{
private :
// Polygon to triangulate. Assumed to be closed (i.e. last edge is (LastVertex, FirstVertex)). If Polygon has
// self-intersections or degenerate edges, result is undefined.
TArray < FFrame3d > Polygon ;
// For each polygon vertex, a scale factor for the patch width at that vertex. Helps keep the width constant
// going around acute corners.
TArray < double > OffsetScaleFactors ;
// Width of quads to generate.
double Width = 1.0 ;
// Normal vector of all vertices will be set to this value. Default is +Z axis.
FVector3d Normal = FVector3d : : UnitZ ( ) ;
public :
FPolygonEdgeMeshGenerator ( const TArray < FFrame3d > & InPolygon ,
const TArray < double > & InOffsetScaleFactors ,
double InWidth = 1.0 ,
FVector3d InNormal = FVector3d : : UnitZ ( ) ) :
Polygon ( InPolygon ) ,
OffsetScaleFactors ( InOffsetScaleFactors ) ,
Width ( InWidth ) ,
Normal ( InNormal )
{
check ( Polygon . Num ( ) = = OffsetScaleFactors . Num ( ) ) ;
}
// Generate the triangulation
virtual FMeshShapeGenerator & Generate ( ) final
{
const int NumInputVertices = Polygon . Num ( ) ;
check ( NumInputVertices > = 3 ) ;
if ( NumInputVertices < 3 )
{
return * this ;
}
const int NumVertices = 2 * NumInputVertices ;
const int NumTriangles = 2 * NumInputVertices ;
SetBufferSizes ( NumVertices , NumTriangles , NumVertices , NumVertices ) ;
// Trace the input path, placing vertices on either side of each input vertex
const FVector3d LeftVertex { 0 , - Width , 0 } ;
const FVector3d RightVertex { 0 , Width , 0 } ;
for ( int CurrentInputVertex = 0 ; CurrentInputVertex < NumInputVertices ; + + CurrentInputVertex )
{
const FFrame3d & CurrentFrame = Polygon [ CurrentInputVertex ] ;
const int NewVertexAIndex = 2 * CurrentInputVertex ;
const int NewVertexBIndex = NewVertexAIndex + 1 ;
Vertices [ NewVertexAIndex ] = CurrentFrame . FromFramePoint ( OffsetScaleFactors [ CurrentInputVertex ] * LeftVertex ) ;
Vertices [ NewVertexBIndex ] = CurrentFrame . FromFramePoint ( OffsetScaleFactors [ CurrentInputVertex ] * RightVertex ) ;
}
// Triangulate the vertices we just placed
for ( int CurrentVertex = 0 ; CurrentVertex < NumInputVertices ; + + CurrentVertex )
{
const int NewVertexA = 2 * CurrentVertex ;
const int NewVertexB = NewVertexA + 1 ;
const int NewVertexC = ( NewVertexA + 2 ) % NumVertices ;
const int NewVertexD = ( NewVertexA + 3 ) % NumVertices ;
FIndex3i NewTriA { NewVertexA , NewVertexB , NewVertexC } ;
const int NewTriAIndex = 2 * CurrentVertex ;
SetTriangle ( NewTriAIndex , NewTriA ) ;
SetTriangleUVs ( NewTriAIndex , NewTriA ) ;
SetTriangleNormals ( NewTriAIndex , NewTriA ) ;
SetTrianglePolygon ( NewTriAIndex , 0 ) ;
const int NewTriBIndex = NewTriAIndex + 1 ;
FIndex3i NewTriB { NewVertexC , NewVertexB , NewVertexD } ;
SetTriangle ( NewTriBIndex , NewTriB ) ;
SetTriangleUVs ( NewTriBIndex , NewTriB ) ;
SetTriangleNormals ( NewTriBIndex , NewTriB ) ;
SetTrianglePolygon ( NewTriBIndex , 0 ) ;
}
// Set UVs, etc. for the vertices we put down previously
FAxisAlignedBox2d BoundingBox ;
for ( auto & CurrentFrame : Polygon )
{
BoundingBox . Contain ( FVector2d { CurrentFrame . Origin . X , CurrentFrame . Origin . Y } ) ;
}
BoundingBox . Max + = { Width , Width } ;
BoundingBox . Min - = { Width , Width } ;
double BoxWidth = BoundingBox . Width ( ) , BoxHeight = BoundingBox . Height ( ) ;
double UVScale = FMath : : Max ( BoxWidth , BoxHeight ) ;
for ( int NewVertexIndex = 0 ; NewVertexIndex < NumVertices ; + + NewVertexIndex )
{
FVector3d Pos = Vertices [ NewVertexIndex ] ;
UVs [ NewVertexIndex ] = FVector2f (
( float ) ( ( Pos . X - BoundingBox . Min . X ) / UVScale ) ,
( float ) ( ( Pos . Y - BoundingBox . Min . Y ) / UVScale ) ) ;
UVParentVertex [ NewVertexIndex ] = NewVertexIndex ;
Normals [ NewVertexIndex ] = FVector3f ( Normal ) ;
NormalParentVertex [ NewVertexIndex ] = NewVertexIndex ;
}
return * this ;
}
} ; // class FPolygonEdgeMeshGenerator
} // unnamed namespace
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 ) ;
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 ) ;
AddInputBehavior ( ClickBehavior ) ;
UMouseHoverBehavior * HoverBehavior = NewObject < UMouseHoverBehavior > ( this ) ;
HoverBehavior - > Initialize ( this ) ;
AddInputBehavior ( HoverBehavior ) ;
DrawPlaneWorld = FFrame3d ( ) ;
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 ) ;
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-02-08 17:02:09 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Extrude PolyPath " ) ) ;
2020-03-10 14:00:36 -04:00
ShowStartupMessage ( ) ;
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : Shutdown ( EToolShutdownType ShutdownType )
{
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 ( ) ;
}
void UDrawPolyPathTool : : RegisterActions ( FInteractiveToolActionSet & ActionSet )
{
//ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 1,
// TEXT("PopLastVertex"),
// LOCTEXT("PopLastVertex", "Pop Last Vertex"),
// LOCTEXT("PopLastVertexTooltip", "Pop last vertex added to polygon"),
// EModifierKey::None, EKeys::BackSpace,
// [this]() { PopLastVertexAction(); });
//ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 2,
// TEXT("ToggleGizmo"),
// LOCTEXT("ToggleGizmo", "Toggle Gizmo"),
// LOCTEXT("ToggleGizmoTooltip", "Toggle visibility of the transformation Gizmo"),
// EModifierKey::None, EKeys::A,
// [this]() { PolygonProperties->bShowGizmo = !PolygonProperties->bShowGizmo; });
}
bool UDrawPolyPathTool : : HitTest ( const FRay & Ray , FHitResult & OutHit )
{
if ( SurfacePathMechanic ! = nullptr )
{
FFrame3d HitPoint ;
if ( SurfacePathMechanic - > IsHitByRay ( FRay3d ( Ray ) , HitPoint ) )
{
OutHit . Distance = FRay3d ( Ray ) . Project ( HitPoint . Origin ) ;
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 ;
}
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 )
{
if ( SurfacePathMechanic ! = nullptr )
{
bool bIsFirstPoint = SurfacePathMechanic - > HitPath . Num ( ) = = 0 ;
if ( SurfacePathMechanic - > TryAddPointFromRay ( ClickPos . WorldRay ) )
{
if ( SurfacePathMechanic - > IsDone ( ) )
{
2020-03-09 13:52:38 -04:00
bPathIsClosed = SurfacePathMechanic - > LoopWasClosed ( ) ;
2020-01-27 20:11:15 -05:00
GetToolManager ( ) - > EmitObjectChange ( this , MakeUnique < FDrawPolyPathStateChange > ( CurrentCurveTimestamp ) , LOCTEXT ( " DrawPolyPathBeginOffset " , " Set Offset " ) ) ;
OnCompleteSurfacePath ( ) ;
}
else
{
GetToolManager ( ) - > EmitObjectChange ( this , MakeUnique < FDrawPolyPathStateChange > ( CurrentCurveTimestamp ) , LOCTEXT ( " DrawPolyPathBeginPath " , " Begin Path " ) ) ;
}
}
}
else if ( CurveDistMechanic ! = nullptr )
{
GetToolManager ( ) - > EmitObjectChange ( this , MakeUnique < FDrawPolyPathStateChange > ( CurrentCurveTimestamp ) , LOCTEXT ( " DrawPolyPathBeginHeight " , " Set Height " ) ) ;
OnCompleteOffsetDistance ( ) ;
}
else if ( ExtrudeHeightMechanic ! = nullptr )
{
OnCompleteExtrudeHeight ( ) ;
}
}
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 )
{
if ( SurfacePathMechanic ! = nullptr )
{
SurfacePathMechanic - > UpdatePreviewPoint ( DevicePos . WorldRay ) ;
}
else if ( CurveDistMechanic ! = nullptr )
{
CurveDistMechanic - > UpdateCurrentDistance ( DevicePos . WorldRay ) ;
TransformProps - > Width = CurveDistMechanic - > CurrentDistance ;
CurOffsetDistance = CurveDistMechanic - > CurrentDistance ;
UpdatePathPreview ( ) ;
}
else if ( ExtrudeHeightMechanic ! = nullptr )
{
ExtrudeHeightMechanic - > UpdateCurrentDistance ( DevicePos . WorldRay ) ;
CurHeight = ExtrudeHeightMechanic - > CurrentHeight ;
TransformProps - > Height = ExtrudeHeightMechanic - > CurrentHeight ;
UpdateExtrudePreview ( ) ;
}
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 - > SetEnableGridSnaping ( TransformProps - > bSnapToWorldGrid ) ;
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 ) ;
}
}
void UDrawPolyPathTool : : InitializeNewSurfacePath ( )
{
SurfacePathMechanic = NewObject < UCollectSurfacePathMechanic > ( this ) ;
SurfacePathMechanic - > Setup ( this ) ;
double SnapTol = ToolSceneQueriesUtil : : GetDefaultVisualAngleSnapThreshD ( ) ;
SurfacePathMechanic - > SpatialSnapPointsFunc = [ this , SnapTol ] ( FVector3d Position1 , FVector3d Position2 )
{
2020-06-23 18:40:00 -04:00
return true & & 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 ( ) ;
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
{
return ( SurfacePathMechanic ! = nullptr & & SurfacePathMechanic - > HitPath . Num ( ) = = 0 ) ;
}
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 ;
ArcLengths . SetNum ( NumPoints ) ;
ArcLengths [ 0 ] = 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 ) ;
2021-03-30 21:25:22 -04:00
ArcLengths [ j ] = ArcLengths [ PrevJ ] + Distance ( Cur , Prev ) ;
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 ) ;
if ( Line1 . Direction . Dot ( Line2 . Direction ) > 0.999 )
{
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 ) ;
}
}
ArcLengths [ NumPoints - 1 ] = ArcLengths [ NumPoints - 2 ] + CurPathPoints [ NumPoints - 1 ] . Origin . Distance ( CurPathPoints [ NumPoints - 2 ] . Origin ) ;
CurPolyLine . Reset ( ) ;
for ( const FFrame3d & Point : SurfacePathMechanic - > HitPath )
{
CurPolyLine . Add ( Point . Origin ) ;
}
SurfacePathMechanic = nullptr ;
if ( TransformProps - > WidthMode = = EDrawPolyPathWidthMode : : Constant )
{
BeginConstantOffsetDistance ( ) ;
}
else
{
BeginInteractiveOffsetDistance ( ) ;
}
}
void UDrawPolyPathTool : : BeginInteractiveOffsetDistance ( )
{
// begin setting offset distance
CurveDistMechanic = NewObject < USpatialCurveDistanceMechanic > ( this ) ;
CurveDistMechanic - > Setup ( this ) ;
2021-03-18 12:46:27 -04:00
CurveDistMechanic - > InitializePolyCurve ( CurPolyLine , UE : : Geometry : : FTransform3d : : Identity ( ) ) ;
2020-01-27 20:11:15 -05:00
InitializePreviewMesh ( ) ;
2020-03-10 14:00:36 -04:00
ShowOffsetMessage ( ) ;
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : BeginConstantOffsetDistance ( )
{
InitializePreviewMesh ( ) ;
CurOffsetDistance = TransformProps - > Width ;
UpdatePathPreview ( ) ;
OnCompleteOffsetDistance ( ) ;
}
void UDrawPolyPathTool : : OnCompleteOffsetDistance ( )
{
CurveDistMechanic = nullptr ;
if ( TransformProps - > OutputType = = EDrawPolyPathOutputMode : : Ribbon )
{
OnCompleteExtrudeHeight ( ) ;
}
else if ( TransformProps - > HeightMode = = EDrawPolyPathHeightMode : : Constant )
{
CurHeight = TransformProps - > Height ;
OnCompleteExtrudeHeight ( ) ;
}
else
{
BeginInteractiveExtrudeHeight ( ) ;
}
}
void UDrawPolyPathTool : : OnCompleteExtrudeHeight ( )
{
CurHeight = TransformProps - > Height ;
ExtrudeHeightMechanic = nullptr ;
ClearPreview ( ) ;
EmitNewObject ( TransformProps - > OutputType ) ;
InitializeNewSurfacePath ( ) ;
CurrentCurveTimestamp + + ;
}
void UDrawPolyPathTool : : UpdatePathPreview ( )
{
check ( EditPreview ! = nullptr ) ;
FDynamicMesh3 PathMesh ;
GeneratePathMesh ( PathMesh ) ;
EditPreview - > ReplaceMesh ( MoveTempIfPossible ( PathMesh ) ) ;
}
void UDrawPolyPathTool : : GeneratePathMesh ( FDynamicMesh3 & Mesh )
{
Mesh . Clear ( ) ;
int32 NumPoints = CurPathPoints . Num ( ) ;
if ( NumPoints > 1 )
{
2020-03-09 13:52:38 -04:00
if ( bPathIsClosed )
2020-01-27 20:11:15 -05:00
{
2020-03-09 13:52:38 -04:00
FPolygonEdgeMeshGenerator MeshGen ( CurPathPoints , OffsetScaleFactors , CurOffsetDistance , FVector3d : : UnitZ ( ) ) ;
MeshGen . Generate ( ) ;
Mesh . Copy ( & MeshGen ) ;
2020-01-27 20:11:15 -05:00
}
2020-03-09 13:52:38 -04:00
else
2020-01-27 20:11:15 -05:00
{
2020-03-09 13:52:38 -04:00
CurPathLength = 0 ;
for ( int32 k = 1 ; k < NumPoints ; + + k )
{
CurPathLength + = CurPathPoints [ k ] . Origin . Distance ( CurPathPoints [ k - 1 ] . Origin ) ;
}
FRectangleMeshGenerator MeshGen ;
MeshGen . Width = CurPathLength ;
MeshGen . Height = 2 * CurOffsetDistance ;
MeshGen . Normal = FVector3f : : UnitZ ( ) ;
MeshGen . Origin = FVector3d ( CurPathLength / 2 , 0 , 0 ) ;
MeshGen . HeightVertexCount = 2 ;
MeshGen . WidthVertexCount = NumPoints ;
MeshGen . Generate ( ) ;
Mesh . Copy ( & MeshGen ) ;
Mesh . EnableVertexUVs ( FVector2f : : Zero ( ) ) ; // we will store arc length for each vtx in VertexUV
FAxisAlignedBox3d PathBounds = Mesh . GetBounds ( ) ;
double ShiftX = 0 ;
double DeltaX = CurPathLength / ( double ) ( NumPoints - 1 ) ;
for ( int32 k = 0 ; k < NumPoints ; + + k )
{
FFrame3d PathFrame = CurPathPoints [ k ] ;
FVector3d V0 = Mesh . GetVertex ( k ) ;
V0 . X - = ShiftX ;
V0 . Y * = OffsetScaleFactors [ k ] ;
V0 = PathFrame . FromFramePoint ( V0 ) ;
Mesh . SetVertex ( k , V0 ) ;
Mesh . SetVertexUV ( k , FVector2f ( ( float ) ArcLengths [ k ] , ( float ) k ) ) ;
FVector3d V1 = Mesh . GetVertex ( NumPoints + k ) ;
V1 . X - = ShiftX ;
V1 . Y * = OffsetScaleFactors [ k ] ;
V1 = PathFrame . FromFramePoint ( V1 ) ;
Mesh . SetVertex ( NumPoints + k , V1 ) ;
Mesh . SetVertexUV ( NumPoints + k , FVector2f ( ( float ) ArcLengths [ k ] , ( float ) k ) ) ;
ShiftX + = DeltaX ;
}
2020-01-27 20:11:15 -05:00
}
2020-03-03 16:57:38 -05:00
FMeshNormals : : QuickRecomputeOverlayNormals ( Mesh ) ;
2020-01-27 20:11:15 -05:00
}
}
void UDrawPolyPathTool : : BeginInteractiveExtrudeHeight ( )
{
// begin extrude
ExtrudeHeightMechanic = NewObject < UPlaneDistanceFromHitMechanic > ( this ) ;
ExtrudeHeightMechanic - > Setup ( this ) ;
ExtrudeHeightMechanic - > WorldHitQueryFunc = [ this ] ( const FRay & WorldRay , FHitResult & HitResult )
{
2020-10-22 19:19:16 -04:00
return ToolSceneQueriesUtil : : FindNearestVisibleObjectHit ( TargetWorld , HitResult , WorldRay ) ;
2020-01-27 20:11:15 -05:00
} ;
ExtrudeHeightMechanic - > WorldPointSnapFunc = [ this ] ( const FVector3d & WorldPos , FVector3d & SnapPos )
{
return TransformProps - > bSnapToWorldGrid & & ToolSceneQueriesUtil : : FindWorldGridSnapPoint ( this , WorldPos , SnapPos ) ;
} ;
ExtrudeHeightMechanic - > CurrentHeight = 1.0f ; // initialize to something non-zero...prob should be based on polygon bounds maybe?
InitializePreviewMesh ( ) ;
FDynamicMesh3 PathMesh ;
GeneratePathMesh ( PathMesh ) ;
EditPreview - > InitializeExtrudeType ( MoveTemp ( PathMesh ) , DrawPlaneWorld . Z ( ) , nullptr , false ) ;
FDynamicMesh3 TmpMesh ;
EditPreview - > MakeExtrudeTypeHitTargetMesh ( TmpMesh , false ) ;
FFrame3d UseFrame = DrawPlaneWorld ;
UseFrame . Origin = CurPathPoints . Last ( ) . Origin ;
ExtrudeHeightMechanic - > Initialize ( MoveTemp ( TmpMesh ) , UseFrame , true ) ;
2020-03-10 14:00:36 -04:00
ShowExtrudeMessage ( ) ;
2020-01-27 20:11:15 -05:00
}
void UDrawPolyPathTool : : UpdateExtrudePreview ( )
{
if ( TransformProps - > OutputType = = EDrawPolyPathOutputMode : : Ramp )
{
EditPreview - > UpdateExtrudeType ( [ & ] ( FDynamicMesh3 & Mesh ) { GenerateRampMesh ( Mesh ) ; } , true ) ;
}
else
{
EditPreview - > UpdateExtrudeType ( [ & ] ( FDynamicMesh3 & Mesh ) { GenerateExtrudeMesh ( Mesh ) ; } , true ) ;
}
}
void UDrawPolyPathTool : : InitializePreviewMesh ( )
{
if ( EditPreview = = nullptr )
{
EditPreview = NewObject < UPolyEditPreviewMesh > ( this ) ;
EditPreview - > CreateInWorld ( TargetWorld , FTransform : : Identity ) ;
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 ;
}
}
void UDrawPolyPathTool : : GenerateExtrudeMesh ( FDynamicMesh3 & PathMesh )
{
FAxisAlignedBox3d Bounds = PathMesh . GetBounds ( ) ;
FExtrudeMesh Extruder ( & PathMesh ) ;
FVector3d ExtrudeDir = DrawPlaneWorld . Z ( ) ;
Extruder . ExtrudedPositionFunc = [ & ] ( const FVector3d & P , const FVector3f & N , int32 VID ) {
return P + CurHeight * ExtrudeDir ;
} ;
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
}
void UDrawPolyPathTool : : GenerateRampMesh ( FDynamicMesh3 & PathMesh )
{
FAxisAlignedBox3d Bounds = PathMesh . GetBounds ( ) ;
FExtrudeMesh Extruder ( & PathMesh ) ;
FVector3d ExtrudeDir = DrawPlaneWorld . Z ( ) ;
int NumPoints = CurPathPoints . Num ( ) ;
double RampStartRatio = TransformProps - > RampStartRatio ;
double StartHeight = FMathd : : Max ( 0.1 , RampStartRatio * FMathd : : Abs ( CurHeight ) ) * FMathd : : Sign ( CurHeight ) ;
double EndHeight = CurHeight ;
Extruder . ExtrudedPositionFunc = [ & ] ( const FVector3d & P , const FVector3f & N , int32 VID ) {
FVector2f UV = Extruder . Mesh - > GetVertexUV ( VID ) ;
double UseHeight = FMathd : : Lerp ( StartHeight , EndHeight , ( UV . X / this - > CurPathLength ) ) ;
return P + UseHeight * ExtrudeDir ;
} ;
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
}
void UDrawPolyPathTool : : EmitNewObject ( EDrawPolyPathOutputMode OutputMode )
{
FDynamicMesh3 PathMesh ;
GeneratePathMesh ( PathMesh ) ;
if ( OutputMode = = EDrawPolyPathOutputMode : : Extrusion )
{
GenerateExtrudeMesh ( PathMesh ) ;
}
else if ( OutputMode = = EDrawPolyPathOutputMode : : Ramp )
{
GenerateRampMesh ( PathMesh ) ;
}
PathMesh . DiscardVertexUVs ( ) ; // throw away arc lengths
FFrame3d MeshTransform = DrawPlaneWorld ;
FVector3d Center = PathMesh . GetBounds ( ) . Center ( ) ;
MeshTransform . Origin = MeshTransform . ToPlane ( Center , 2 ) ;
MeshTransforms : : WorldToFrameCoords ( PathMesh , MeshTransform ) ;
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " CreatePolyPath " , " Create PolyPath " ) ) ;
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 ( ) ;
}
2020-03-10 14:00:36 -04:00
void UDrawPolyPathTool : : ShowStartupMessage ( )
{
GetToolManager ( ) - > DisplayMessage (
2021-04-20 11:59:49 -04:00
LOCTEXT ( " OnStartDraw " , " Use this Tool to draw a path on the Drawing Plane, and then Extrude. Left-click to place points, Double-click (or close) to complete path. Ctrl-click on the scene to reposition the Plane (Shift+Ctrl-click to ignore Normal). " ) ,
2020-03-10 14:00:36 -04:00
EToolMessageLevel : : UserNotification ) ;
}
void UDrawPolyPathTool : : ShowOffsetMessage ( )
{
GetToolManager ( ) - > DisplayMessage (
LOCTEXT ( " OnStartOffset " , " Set the Width of the Extrusion by clicking on the Drawing Plane. " ) ,
EToolMessageLevel : : UserNotification ) ;
}
void UDrawPolyPathTool : : ShowExtrudeMessage ( )
{
GetToolManager ( ) - > DisplayMessage (
LOCTEXT ( " OnStartExtrude " , " Set the Height of the Extrusion by positioning the mouse over the extrusion volume, or over the scene to snap to relative heights. " ) ,
EToolMessageLevel : : UserNotification ) ;
}
2020-01-27 20:11:15 -05:00
void UDrawPolyPathTool : : UndoCurrentOperation ( )
{
if ( SurfacePathMechanic ! = nullptr )
{
SurfacePathMechanic - > PopLastPoint ( ) ;
if ( SurfacePathMechanic - > HitPath . Num ( ) = = 0 )
{
CurrentCurveTimestamp + + ;
}
}
else if ( CurveDistMechanic ! = nullptr )
{
CurveDistMechanic = nullptr ;
ClearPreview ( ) ;
InitializeNewSurfacePath ( ) ;
SurfacePathMechanic - > HitPath = CurPathPoints ;
}
else if ( ExtrudeHeightMechanic ! = nullptr )
{
ExtrudeHeightMechanic = nullptr ;
BeginInteractiveOffsetDistance ( ) ;
}
}
void FDrawPolyPathStateChange : : Revert ( UObject * Object )
{
Cast < UDrawPolyPathTool > ( Object ) - > UndoCurrentOperation ( ) ;
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