// Copyright Epic Games, Inc. All Rights Reserved. #include "OffsetMeshTool.h" #include "InteractiveToolManager.h" #include "ToolBuilderUtil.h" #include "DynamicMesh/DynamicMesh3.h" #include "DynamicMesh/DynamicMeshAABBTree3.h" #include "WeightMapTypes.h" #include "DeformationOps/MeshOffsetOps.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(OffsetMeshTool) using namespace UE::Geometry; #define LOCTEXT_NAMESPACE "UOffsetMeshTool" /* * Tool Builder */ USingleTargetWithSelectionTool* UOffsetMeshToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const { return NewObject(SceneState.ToolManager); } /* * Tool */ UOffsetMeshTool::UOffsetMeshTool() { SetToolDisplayName(LOCTEXT("ToolName", "Offset")); } void UOffsetMeshTool::InitializeProperties() { OffsetProperties = NewObject(this); AddToolPropertySource(OffsetProperties); OffsetProperties->RestoreProperties(this); OffsetProperties->WatchProperty(OffsetProperties->OffsetType, [&](EOffsetMeshToolOffsetType) { UpdateOptionalPropertyVisibility(); InvalidateResult(); }); OffsetProperties->WatchProperty(OffsetProperties->Distance, [&](float) { InvalidateResult(); }); OffsetProperties->WatchProperty(OffsetProperties->bCreateShell, [&](bool) { InvalidateResult(); }); IterativeProperties = AddOptionalPropertySet( [&]() { return OffsetProperties->OffsetType == EOffsetMeshToolOffsetType::Iterative; }); ImplicitProperties = AddOptionalPropertySet( [&]() { return OffsetProperties->OffsetType == EOffsetMeshToolOffsetType::Implicit; }); WeightMapProperties = AddWeightMapPropertySet(); WeightMapProperties->WatchProperty(WeightMapProperties->MinDistance, [&](float) { InvalidateResult(); }); } void UOffsetMeshTool::OnShutdown(EToolShutdownType ShutdownType) { OffsetProperties->SaveProperties(this); } FText UOffsetMeshTool::GetToolMessageString() const { return LOCTEXT("StartOffsetToolMessage", "Offset the mesh vertex positions using various Offsetting methods."); } FText UOffsetMeshTool::GetAcceptTransactionName() const { return LOCTEXT("OffsetMeshToolTransactionName", "Offset Mesh"); } bool UOffsetMeshTool::HasMeshTopologyChanged() const { return OffsetProperties->bCreateShell; } TUniquePtr UOffsetMeshTool::MakeNewOperator() { const FDynamicMesh3* Mesh = &GetInitialMesh(); TUniquePtr MeshOp; switch (OffsetProperties->OffsetType) { default: case EOffsetMeshToolOffsetType::Iterative: { TUniquePtr IterOp = MakeUnique(Mesh); IterOp->Steps = IterativeProperties->Steps; IterOp->SmoothAlpha = IterativeProperties->SmoothingPerStep; IterOp->bReprojectSmooth = IterativeProperties->bReprojectSmooth; IterOp->bFixedBoundary = (!IterativeProperties->bOffsetBoundaries); MeshOp = MoveTemp(IterOp); } break; case EOffsetMeshToolOffsetType::Implicit: { TUniquePtr ImplicitOp = MakeUnique(Mesh); double NonlinearT = FMathd::Pow(ImplicitProperties->Smoothness, 2.0); // this is an empirically-determined hack that seems to work OK to normalize the result for variable vertex count... double ScaledPower = (NonlinearT / 50.0) * Mesh->VertexCount(); ImplicitOp->Softness = ScaledPower; ImplicitOp->bUniformWeights = (ImplicitProperties->bPreserveUVs == false); ImplicitOp->bFixedBoundary = true; MeshOp = MoveTemp(ImplicitOp); } break; } double MaxDist = FMathd::Abs(OffsetProperties->Distance); double MinDist = FMathd::Abs(WeightMapProperties->MinDistance); MinDist = FMathd::Min(MinDist, MaxDist); MaxDist *= GetScaleNormalizationFactor(); MinDist *= GetScaleNormalizationFactor(); MeshOp->OffsetRange = FInterval1d(MinDist, MaxDist); MeshOp->OffsetSign = (OffsetProperties->Distance < 0) ? -1.0 : 1.0; MeshOp->BaseNormals = this->GetInitialVtxNormals(); MeshOp->bCreateShell = OffsetProperties->bCreateShell; if (MeshOp->bCreateShell) { MeshOp->BoundaryLoops = GetInitialBoundaryLoops(); } MeshOp->WeightMap = GetActiveWeightMap(); MeshOp->bUseWeightMap = true; MeshOp->SetTransform( FTransform3d(GetPreviewTransform()) ); return MoveTemp(MeshOp); } #undef LOCTEXT_NAMESPACE