2021-03-22 08:32:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "CutMeshWithMeshTool.h"
# include "CompositionOps/BooleanMeshesOp.h"
# include "ToolSetupUtil.h"
# include "Selection/ToolSelectionUtil.h"
# include "AssetGenerationUtil.h"
# include "DynamicMesh3.h"
# include "MeshTransforms.h"
# include "MeshDescriptionToDynamicMesh.h"
# include "DynamicMeshToMeshDescription.h"
# include "Async/Async.h"
2021-03-22 11:13:15 -04:00
# include "TargetInterfaces/MaterialProvider.h"
# include "TargetInterfaces/MeshDescriptionCommitter.h"
# include "TargetInterfaces/MeshDescriptionProvider.h"
# include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
2021-03-23 14:51:49 -04:00
# include "TargetInterfaces/AssetBackedTarget.h"
2021-03-22 11:13:15 -04:00
# include "ToolTargetManager.h"
# include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types)
using namespace UE : : Geometry ;
2021-03-22 08:32:17 -04:00
namespace
{
// probably should be something defined for the whole tool framework...
# if WITH_EDITOR
static EAsyncExecution CutMeshWithMeshToolAsyncExecTarget = EAsyncExecution : : LargeThreadPool ;
# else
static EAsyncExecution CutMeshWithMeshToolAsyncExecTarget = EAsyncExecution : : ThreadPool ;
# endif
}
# define LOCTEXT_NAMESPACE "UCutMeshWithMeshTool"
void UCutMeshWithMeshTool : : SetupProperties ( )
{
Super : : SetupProperties ( ) ;
CutProperties = NewObject < UCutMeshWithMeshToolProperties > ( this ) ;
CutProperties - > RestoreProperties ( this ) ;
AddToolPropertySource ( CutProperties ) ;
SetToolDisplayName ( LOCTEXT ( " CutMeshWithMeshToolName " , " Cut With Mesh " ) ) ;
GetToolManager ( ) - > DisplayMessage (
LOCTEXT ( " OnStartTool " , " Cut first mesh with second. Use the transform gizmos to tweak the positions of the input objects (can help to resolve errors/failures) " ) ,
EToolMessageLevel : : UserNotification ) ;
// create intersection preview mesh object
IntersectPreviewMesh = NewObject < UPreviewMesh > ( this ) ;
IntersectPreviewMesh - > CreateInWorld ( TargetWorld , FTransform : : Identity ) ;
IntersectPreviewMesh - > SetVisible ( true ) ;
//IntersectPreviewMesh->SetMaterial(MaterialProperties->Material.Get());
//IntersectPreviewMesh->SetMaterial(ToolSetupUtil::GetDefaultMaterial(nullptr));
IntersectPreviewMesh - > SetMaterial ( ToolSetupUtil : : GetDefaultBrushVolumeMaterial ( GetToolManager ( ) ) ) ;
}
void UCutMeshWithMeshTool : : SaveProperties ( )
{
Super : : SaveProperties ( ) ;
CutProperties - > SaveProperties ( this ) ;
IntersectPreviewMesh - > Disconnect ( ) ;
}
void UCutMeshWithMeshTool : : ConvertInputsAndSetPreviewMaterials ( bool bSetPreviewMesh )
{
// disable output options
// (this property set is not registered yet in SetupProperties() above)
SetToolPropertySourceEnabled ( HandleSourcesProperties , false ) ;
FComponentMaterialSet AllMaterialSet ;
TMap < UMaterialInterface * , int > KnownMaterials ;
2021-03-22 11:13:15 -04:00
TArray < TArray < int > > MaterialRemap ; MaterialRemap . SetNum ( Targets . Num ( ) ) ;
2021-03-22 08:32:17 -04:00
if ( ! CutProperties - > bOnlyUseFirstMeshMaterials )
{
2021-03-22 11:13:15 -04:00
for ( int ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2021-03-22 08:32:17 -04:00
{
FComponentMaterialSet ComponentMaterialSet ;
2021-03-22 11:13:15 -04:00
TargetMaterialInterface ( ComponentIdx ) - > GetMaterialSet ( ComponentMaterialSet ) ;
2021-03-22 08:32:17 -04:00
for ( UMaterialInterface * Mat : ComponentMaterialSet . Materials )
{
int * FoundMatIdx = KnownMaterials . Find ( Mat ) ;
int MatIdx ;
if ( FoundMatIdx )
{
MatIdx = * FoundMatIdx ;
}
else
{
MatIdx = AllMaterialSet . Materials . Add ( Mat ) ;
KnownMaterials . Add ( Mat , MatIdx ) ;
}
MaterialRemap [ ComponentIdx ] . Add ( MatIdx ) ;
}
}
}
else
{
2021-03-22 11:13:15 -04:00
TargetMaterialInterface ( 0 ) - > GetMaterialSet ( AllMaterialSet ) ;
2021-03-22 08:32:17 -04:00
for ( int MatIdx = 0 ; MatIdx < AllMaterialSet . Materials . Num ( ) ; MatIdx + + )
{
MaterialRemap [ 0 ] . Add ( MatIdx ) ;
}
2021-03-22 11:13:15 -04:00
for ( int ComponentIdx = 1 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2021-03-22 08:32:17 -04:00
{
2021-03-22 11:13:15 -04:00
MaterialRemap [ ComponentIdx ] . Init ( 0 , TargetMaterialInterface ( ComponentIdx ) - > GetNumMaterials ( ) ) ;
2021-03-22 08:32:17 -04:00
}
}
2021-03-22 11:13:15 -04:00
for ( int ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2021-03-22 08:32:17 -04:00
{
TSharedPtr < FDynamicMesh3 , ESPMode : : ThreadSafe > Mesh = MakeShared < FDynamicMesh3 , ESPMode : : ThreadSafe > ( ) ;
FMeshDescriptionToDynamicMesh Converter ;
2021-03-22 11:13:15 -04:00
Converter . Convert ( TargetMeshProviderInterface ( ComponentIdx ) - > GetMeshDescription ( ) , * Mesh ) ;
2021-03-22 08:32:17 -04:00
// ensure materials and attributes are always enabled
Mesh - > EnableAttributes ( ) ;
Mesh - > Attributes ( ) - > EnableMaterialID ( ) ;
FDynamicMeshMaterialAttribute * MaterialIDs = Mesh - > Attributes ( ) - > GetMaterialID ( ) ;
for ( int TID : Mesh - > TriangleIndicesItr ( ) )
{
MaterialIDs - > SetValue ( TID , MaterialRemap [ ComponentIdx ] [ MaterialIDs - > GetValue ( TID ) ] ) ;
}
if ( ComponentIdx = = 0 )
{
OriginalTargetMesh = Mesh ;
}
else
{
OriginalCuttingMesh = Mesh ;
}
}
Preview - > ConfigureMaterials ( AllMaterialSet . Materials , ToolSetupUtil : : GetDefaultWorkingMaterial ( GetToolManager ( ) ) ) ;
// check if we have the same mesh on both inputs
2021-03-23 14:51:49 -04:00
if ( Cast < IAssetBackedTarget > ( Targets [ 0 ] ) - > HasSameSourceData ( Targets [ 1 ] ) )
{
GetToolManager ( ) - > DisplayMessage (
LOCTEXT ( " SameSourceError " , " WARNING: Target Mesh has same Asset as Cutting Mesh, both inputs will be affected " ) ,
EToolMessageLevel : : UserWarning ) ;
}
2021-03-22 08:32:17 -04:00
}
class FCutMeshWithMeshOp : public FDynamicMeshOperator
{
public :
virtual ~ FCutMeshWithMeshOp ( ) { }
TSharedPtr < const FDynamicMesh3 , ESPMode : : ThreadSafe > TargetMesh ;
FTransform TargetMeshTransform ;
TSharedPtr < const FDynamicMesh3 , ESPMode : : ThreadSafe > CuttingMesh ;
FTransform CuttingMeshTransform ;
bool bAttemptToFixHoles = true ;
bool bCollapseExtraEdges = true ;
virtual void CalculateResult ( FProgressCancel * Progress )
{
TUniquePtr < FBooleanMeshesOp > SubtractOp = MakeUnique < FBooleanMeshesOp > ( ) ;
SubtractOp - > CSGOperation = ECSGOperation : : DifferenceAB ;
SubtractOp - > bAttemptFixHoles = bAttemptToFixHoles ;
SubtractOp - > bTryCollapseExtraEdges = bCollapseExtraEdges ;
SubtractOp - > Meshes . Add ( TargetMesh ) ;
SubtractOp - > Transforms . Add ( TargetMeshTransform ) ;
SubtractOp - > Meshes . Add ( CuttingMesh ) ;
SubtractOp - > Transforms . Add ( CuttingMeshTransform ) ;
TUniquePtr < FBooleanMeshesOp > IntersectOp = MakeUnique < FBooleanMeshesOp > ( ) ;
IntersectOp - > CSGOperation = ECSGOperation : : Intersect ;
IntersectOp - > bAttemptFixHoles = bAttemptToFixHoles ;
IntersectOp - > bTryCollapseExtraEdges = bCollapseExtraEdges ;
IntersectOp - > Meshes . Add ( TargetMesh ) ;
IntersectOp - > Transforms . Add ( TargetMeshTransform ) ;
IntersectOp - > Meshes . Add ( CuttingMesh ) ;
IntersectOp - > Transforms . Add ( CuttingMeshTransform ) ;
TFuture < void > SubtractFuture = Async ( CutMeshWithMeshToolAsyncExecTarget , [ & ] ( )
{
SubtractOp - > CalculateResult ( Progress ) ;
} ) ;
TFuture < void > IntersectFuture = Async ( CutMeshWithMeshToolAsyncExecTarget , [ & ] ( )
{
IntersectOp - > CalculateResult ( Progress ) ;
} ) ;
SubtractFuture . Wait ( ) ;
IntersectFuture . Wait ( ) ;
this - > ResultMesh = SubtractOp - > ExtractResult ( ) ;
SetResultTransform ( SubtractOp - > GetResultTransform ( ) ) ;
IntersectMesh = IntersectOp - > ExtractResult ( ) ;
CreatedSubtractBoundaryEdges = SubtractOp - > GetCreatedBoundaryEdges ( ) ;
CreatedIntersectBoundaryEdges = IntersectOp - > GetCreatedBoundaryEdges ( ) ;
}
TUniquePtr < FDynamicMesh3 > IntersectMesh ;
TArray < int > CreatedSubtractBoundaryEdges ;
TArray < int > CreatedIntersectBoundaryEdges ;
} ;
void UCutMeshWithMeshTool : : SetPreviewCallbacks ( )
{
DrawnLineSet = NewObject < ULineSetComponent > ( Preview - > PreviewMesh - > GetRootComponent ( ) ) ;
DrawnLineSet - > SetupAttachment ( Preview - > PreviewMesh - > GetRootComponent ( ) ) ;
DrawnLineSet - > SetLineMaterial ( ToolSetupUtil : : GetDefaultLineComponentMaterial ( GetToolManager ( ) ) ) ;
DrawnLineSet - > RegisterComponent ( ) ;
Preview - > OnOpCompleted . AddLambda (
[ this ] ( const FDynamicMeshOperator * Op )
{
const FCutMeshWithMeshOp * CuttingOp = ( const FCutMeshWithMeshOp * ) ( Op ) ;
CreatedSubtractBoundaryEdges = CuttingOp - > CreatedSubtractBoundaryEdges ;
CreatedIntersectBoundaryEdges = CuttingOp - > CreatedIntersectBoundaryEdges ;
IntersectionMesh = * CuttingOp - > IntersectMesh ; // cannot steal this here because it is const...
IntersectPreviewMesh - > UpdatePreview ( & IntersectionMesh ) ;
IntersectPreviewMesh - > SetTransform ( ( FTransform ) Op - > GetResultTransform ( ) ) ;
}
) ;
Preview - > OnMeshUpdated . AddLambda (
[ this ] ( const UMeshOpPreviewWithBackgroundCompute * )
{
GetToolManager ( ) - > PostInvalidation ( ) ;
UpdateVisualization ( ) ;
}
) ;
}
void UCutMeshWithMeshTool : : UpdateVisualization ( )
{
FColor BoundaryEdgeColor ( 240 , 15 , 15 ) ;
float BoundaryEdgeThickness = 2.0 ;
float BoundaryEdgeDepthBias = 2.0f ;
DrawnLineSet - > Clear ( ) ;
if ( CutProperties - > bShowNewBoundaryEdges )
{
const FDynamicMesh3 * TargetMesh = Preview - > PreviewMesh - > GetPreviewDynamicMesh ( ) ;
FVector3d A , B ;
for ( int EID : CreatedSubtractBoundaryEdges )
{
TargetMesh - > GetEdgeV ( EID , A , B ) ;
DrawnLineSet - > AddLine ( ( FVector ) A , ( FVector ) B , BoundaryEdgeColor , BoundaryEdgeThickness , BoundaryEdgeDepthBias ) ;
}
for ( int EID : CreatedIntersectBoundaryEdges )
{
IntersectionMesh . GetEdgeV ( EID , A , B ) ;
DrawnLineSet - > AddLine ( ( FVector ) A , ( FVector ) B , BoundaryEdgeColor , BoundaryEdgeThickness , BoundaryEdgeDepthBias ) ;
}
}
}
TUniquePtr < FDynamicMeshOperator > UCutMeshWithMeshTool : : MakeNewOperator ( )
{
TUniquePtr < FCutMeshWithMeshOp > CuttingOp = MakeUnique < FCutMeshWithMeshOp > ( ) ;
CuttingOp - > TargetMesh = OriginalTargetMesh ;
CuttingOp - > TargetMeshTransform = TransformProxies [ 0 ] - > GetTransform ( ) ;
2021-03-22 11:13:15 -04:00
2021-03-22 08:32:17 -04:00
CuttingOp - > CuttingMesh = OriginalCuttingMesh ;
CuttingOp - > CuttingMeshTransform = TransformProxies [ 1 ] - > GetTransform ( ) ;
2021-03-22 11:13:15 -04:00
2021-03-22 08:32:17 -04:00
CuttingOp - > bAttemptToFixHoles = CutProperties - > bAttemptFixHoles ;
CuttingOp - > bCollapseExtraEdges = CutProperties - > bCollapseExtraEdges ;
return CuttingOp ;
}
void UCutMeshWithMeshTool : : OnPropertyModified ( UObject * PropertySet , FProperty * Property )
{
if ( Property & & ( Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UCutMeshWithMeshToolProperties , bOnlyUseFirstMeshMaterials ) ) )
{
if ( ! AreAllTargetsValid ( ) )
{
GetToolManager ( ) - > DisplayMessage ( LOCTEXT ( " InvalidTargets " , " Target meshes are no longer valid " ) , EToolMessageLevel : : UserWarning ) ;
return ;
}
ConvertInputsAndSetPreviewMaterials ( false ) ;
Preview - > InvalidateResult ( ) ;
}
else if ( Property & & ( Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UCutMeshWithMeshToolProperties , bShowNewBoundaryEdges ) ) )
{
GetToolManager ( ) - > PostInvalidation ( ) ;
UpdateVisualization ( ) ;
}
else
{
Super : : OnPropertyModified ( PropertySet , Property ) ;
}
}
FString UCutMeshWithMeshTool : : GetCreatedAssetName ( ) const
{
return TEXT ( " Boolean " ) ;
}
FText UCutMeshWithMeshTool : : GetActionName ( ) const
{
return LOCTEXT ( " CutMeshWithMeshActionName " , " Boolean Meshes " ) ;
}
void UCutMeshWithMeshTool : : Shutdown ( EToolShutdownType ShutdownType )
{
SaveProperties ( ) ;
HandleSourcesProperties - > SaveProperties ( this ) ;
TransformProperties - > SaveProperties ( this ) ;
FDynamicMeshOpResult Result = Preview - > Shutdown ( ) ;
// Restore (unhide) the source meshes
2021-03-22 11:13:15 -04:00
for ( int32 ci = 0 ; ci < Targets . Num ( ) ; + + ci )
2021-03-22 08:32:17 -04:00
{
2021-03-22 11:13:15 -04:00
TargetComponentInterface ( ci ) - > SetOwnerVisibility ( true ) ;
2021-03-22 08:32:17 -04:00
}
if ( ShutdownType = = EToolShutdownType : : Accept )
{
GetToolManager ( ) - > BeginUndoTransaction ( GetActionName ( ) ) ;
TArray < AActor * > SelectActors ;
FComponentMaterialSet MaterialSet ;
MaterialSet . Materials = GetOutputMaterials ( ) ;
// update subtract asset
2021-03-22 11:13:15 -04:00
IPrimitiveComponentBackedTarget * UpdateTarget = TargetComponentInterface ( 0 ) ;
2021-03-22 08:32:17 -04:00
FTransform3d TargetToWorld = ( FTransform3d ) UpdateTarget - > GetWorldTransform ( ) ;
{
if ( Result . Mesh - > TriangleCount ( ) > 0 )
{
MeshTransforms : : ApplyTransform ( * Result . Mesh , Result . Transform ) ;
2021-03-23 17:52:10 -04:00
MeshTransforms : : ApplyTransformInverse ( * Result . Mesh , TargetToWorld ) ;
2021-03-22 11:13:15 -04:00
TargetMeshCommitterInterface ( 0 ) - > CommitMeshDescription ( [ & ] ( const IMeshDescriptionCommitter : : FCommitterParams & CommitParams )
2021-03-22 08:32:17 -04:00
{
FDynamicMeshToMeshDescription Converter ;
2021-03-22 11:13:15 -04:00
Converter . Convert ( Result . Mesh . Get ( ) , * CommitParams . MeshDescriptionOut ) ;
2021-03-22 08:32:17 -04:00
} ) ;
2021-03-22 11:13:15 -04:00
TargetMaterialInterface ( 0 ) - > CommitMaterialSetUpdate ( MaterialSet , true ) ;
2021-03-22 08:32:17 -04:00
}
}
SelectActors . Add ( UpdateTarget - > GetOwnerActor ( ) ) ;
// create intersection asset
if ( IntersectionMesh . TriangleCount ( ) > 0 )
{
MeshTransforms : : ApplyTransform ( IntersectionMesh , Result . Transform ) ;
2021-03-23 17:52:10 -04:00
MeshTransforms : : ApplyTransformInverse ( IntersectionMesh , TargetToWorld ) ;
2021-03-22 08:32:17 -04:00
FTransform3d NewTransform = TargetToWorld ;
2021-03-22 11:13:15 -04:00
FString CurName = AssetGenerationUtil : : GetComponentAssetBaseName ( UpdateTarget - > GetOwnerComponent ( ) ) ;
2021-03-22 08:32:17 -04:00
FString UseBaseName = FString : : Printf ( TEXT ( " %s_%s " ) , * CurName , TEXT ( " CutPart " ) ) ;
TArray < UMaterialInterface * > Materials = GetOutputMaterials ( ) ;
AActor * NewActor = AssetGenerationUtil : : GenerateStaticMeshActor ( AssetAPI , TargetWorld ,
& IntersectionMesh , NewTransform , UseBaseName , Materials ) ;
if ( NewActor ! = nullptr )
{
SelectActors . Add ( NewActor ) ;
}
}
ToolSelectionUtil : : SetNewActorSelection ( GetToolManager ( ) , SelectActors ) ;
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
UInteractiveGizmoManager * GizmoManager = GetToolManager ( ) - > GetPairedGizmoManager ( ) ;
GizmoManager - > DestroyAllGizmosByOwner ( this ) ;
}
# undef LOCTEXT_NAMESPACE