// Copyright Epic Games, Inc. All Rights Reserved. #include "WeldMeshEdgesTool.h" #include "InteractiveToolManager.h" #include "ToolBuilderUtil.h" #include "ToolSetupUtil.h" #include "ModelingToolTargetUtil.h" #include "PreviewMesh.h" #include "Drawing/MeshElementsVisualizer.h" #include "DynamicMesh/DynamicMesh3.h" #include "DynamicMesh/Operations/MergeCoincidentMeshEdges.h" using namespace UE::Geometry; #define LOCTEXT_NAMESPACE "UWeldMeshEdgesTool" /* * ToolBuilder */ USingleSelectionMeshEditingTool* UWeldMeshEdgesToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const { return NewObject(SceneState.ToolManager); } class FWeldMeshEdgesOp : public FDynamicMeshOperator { public: // parameters set by the tool TSharedPtr SourceMesh; double Tolerance; bool bOnlyUnique; public: FWeldMeshEdgesOp() : FDynamicMeshOperator() {} virtual void CalculateResult(FProgressCancel* Progress) override { if ((Progress && Progress->Cancelled()) || !SourceMesh) { return; } if (SourceMesh->TriangleCount() == 0 || SourceMesh->VertexCount() == 0) { return; } ResultMesh->Copy(*SourceMesh, true, true, true, true); FMergeCoincidentMeshEdges Merger(ResultMesh.Get()); Merger.MergeVertexTolerance = Tolerance; Merger.MergeSearchTolerance = 2 * Merger.MergeVertexTolerance; Merger.OnlyUniquePairs = bOnlyUnique; if (Merger.Apply() == false) { ResultMesh->Copy(*SourceMesh, true, true, true, true); } else if (ResultMesh->CheckValidity(true, EValidityCheckFailMode::ReturnOnly) == false) { ResultMesh->Copy(*SourceMesh, true, true, true, true); } } void SetTransform(const FTransform& Transform) { ResultTransform = (UE::Geometry::FTransform3d)Transform; } }; TUniquePtr UWeldMeshEdgesOperatorFactory::MakeNewOperator() { check(WeldMeshEdgesTool); TUniquePtr MeshOp = MakeUnique(); WeldMeshEdgesTool->UpdateOpParameters(*MeshOp); return MeshOp; } /* * Tool */ UWeldMeshEdgesTool::UWeldMeshEdgesTool() { SetToolDisplayName(LOCTEXT("WeldMeshEdgesToolName", "Weld Edges")); } void UWeldMeshEdgesTool::Setup() { UInteractiveTool::Setup(); SourceMesh = MakeShared(UE::ToolTarget::GetDynamicMeshCopy(Target, true)); FTransform MeshTransform = (FTransform)UE::ToolTarget::GetLocalToWorldTransform(Target); OperatorFactory = NewObject(this); OperatorFactory->WeldMeshEdgesTool = this; PreviewCompute = NewObject(OperatorFactory); PreviewCompute->Setup(this->TargetWorld, OperatorFactory); ToolSetupUtil::ApplyRenderingConfigurationToPreview(PreviewCompute->PreviewMesh, Target); // Give the preview something to display PreviewCompute->PreviewMesh->SetTransform(MeshTransform); PreviewCompute->PreviewMesh->SetTangentsMode(EDynamicMeshComponentTangentsMode::ExternallyProvided); PreviewCompute->PreviewMesh->UpdatePreview(SourceMesh.Get()); FComponentMaterialSet MaterialSet = UE::ToolTarget::GetMaterialSet(Target); PreviewCompute->ConfigureMaterials(MaterialSet.Materials, ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager()) ); PreviewCompute->SetVisibility(true); UE::ToolTarget::HideSourceObject(Target); Settings = NewObject(this); Settings->RestoreProperties(this); AddToolPropertySource(Settings); Settings->WatchProperty(Settings->Tolerance, [this](float) { PreviewCompute->InvalidateResult(); }); Settings->WatchProperty(Settings->bOnlyUnique, [this](bool) { PreviewCompute->InvalidateResult(); }); // create mesh display MeshElementsDisplay = NewObject(this); MeshElementsDisplay->CreateInWorld(PreviewCompute->PreviewMesh->GetWorld(), PreviewCompute->PreviewMesh->GetTransform()); if (ensure(MeshElementsDisplay->Settings)) { MeshElementsDisplay->Settings->bShowUVSeams = false; MeshElementsDisplay->Settings->bShowNormalSeams = false; MeshElementsDisplay->Settings->bShowColorSeams = false; MeshElementsDisplay->Settings->RestoreProperties(this, TEXT("WeldEdges")); AddToolPropertySource(MeshElementsDisplay->Settings); } MeshElementsDisplay->SetMeshAccessFunction([this](UMeshElementsVisualizer::ProcessDynamicMeshFunc ProcessFunc) { PreviewCompute->ProcessCurrentMesh(ProcessFunc); }); PreviewCompute->OnMeshUpdated.AddLambda([this](UMeshOpPreviewWithBackgroundCompute* Compute) { Compute->ProcessCurrentMesh([&](const FDynamicMesh3& ReadMesh) { MeshElementsDisplay->NotifyMeshChanged(); }); }); PreviewCompute->InvalidateResult(); SetToolDisplayName(LOCTEXT("ToolName", "Weld Edges")); GetToolManager()->DisplayMessage( LOCTEXT("WeldMeshEdgesToolDescription", "Weld overlapping/identical border edges of the selected Mesh, by merging the vertices."), EToolMessageLevel::UserNotification); } void UWeldMeshEdgesTool::Shutdown(EToolShutdownType ShutdownType) { Settings->SaveProperties(this); UE::ToolTarget::ShowSourceObject(Target); if (ensure(MeshElementsDisplay->Settings)) { MeshElementsDisplay->Settings->SaveProperties(this, TEXT("WeldEdges")); } MeshElementsDisplay->Disconnect(); if (PreviewCompute) { FDynamicMeshOpResult Result = PreviewCompute->Shutdown(); if (ShutdownType == EToolShutdownType::Accept) { GetToolManager()->BeginUndoTransaction(LOCTEXT("WeldMeshEdgesToolTransactionName", "Weld Edges")); FDynamicMesh3* DynamicMeshResult = Result.Mesh.Get(); if (ensure(DynamicMeshResult != nullptr)) { // todo: have not actually modified topology here, but groups-only update is not supported yet UE::ToolTarget::CommitDynamicMeshUpdate(Target, *DynamicMeshResult, true); } GetToolManager()->EndUndoTransaction(); } } } bool UWeldMeshEdgesTool::CanAccept() const { return Super::CanAccept() && (PreviewCompute == nullptr || PreviewCompute->HaveValidResult()); } void UWeldMeshEdgesTool::OnTick(float DeltaTime) { PreviewCompute->Tick(DeltaTime); MeshElementsDisplay->OnTick(DeltaTime); } void UWeldMeshEdgesTool::UpdateOpParameters(FWeldMeshEdgesOp& Op) const { Op.bOnlyUnique = Settings->bOnlyUnique; Op.Tolerance = Settings->Tolerance; Op.SourceMesh = SourceMesh; FTransform LocalToWorld = (FTransform)UE::ToolTarget::GetLocalToWorldTransform(Target); Op.SetTransform(LocalToWorld); } #undef LOCTEXT_NAMESPACE