Files
UnrealEngineUWP/Engine/Plugins/Experimental/MeshModelingToolset/Source/MeshModelingTools/Private/RemeshMeshTool.cpp
Ryan Schmidt 571c91d58f ModelingTools: remove duplicate branch identified by PVS
#rb none
#rnx
#jira none

[CL 16691388 by Ryan Schmidt in ue5-main branch]
2021-06-16 13:26:33 -04:00

308 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RemeshMeshTool.h"
#include "ComponentSourceInterfaces.h"
#include "InteractiveToolManager.h"
#include "ToolBuilderUtil.h"
#include "Properties/RemeshProperties.h"
#include "Properties/MeshStatisticsProperties.h"
#include "Drawing/MeshElementsVisualizer.h"
#include "Util/ColorConstants.h"
#include "ToolSetupUtil.h"
#include "ModelingToolTargetUtil.h"
#include "ToolSetupUtil.h"
#include "DynamicMesh/DynamicMesh3.h"
#include "DynamicMesh/DynamicMeshAABBTree3.h"
#include "MeshDescriptionToDynamicMesh.h"
#include "DynamicMeshToMeshDescription.h"
#include "TargetInterfaces/MaterialProvider.h"
#include "TargetInterfaces/MeshDescriptionCommitter.h"
#include "TargetInterfaces/MeshDescriptionProvider.h"
#include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
#include "ToolTargetManager.h"
#include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types)
using namespace UE::Geometry;
#define LOCTEXT_NAMESPACE "URemeshMeshTool"
/*
* ToolBuilder
*/
const FToolTargetTypeRequirements& URemeshMeshToolBuilder::GetTargetRequirements() const
{
static FToolTargetTypeRequirements TypeRequirements({
UMaterialProvider::StaticClass(),
UMeshDescriptionCommitter::StaticClass(),
UMeshDescriptionProvider::StaticClass(),
UPrimitiveComponentBackedTarget::StaticClass()
});
return TypeRequirements;
}
bool URemeshMeshToolBuilder::CanBuildTool(const FToolBuilderState& SceneState) const
{
return SceneState.TargetManager->CountSelectedAndTargetable(SceneState, GetTargetRequirements()) == 1;
}
UInteractiveTool* URemeshMeshToolBuilder::BuildTool(const FToolBuilderState& SceneState) const
{
URemeshMeshTool* NewTool = NewObject<URemeshMeshTool>(SceneState.ToolManager);
TArray<TObjectPtr<UToolTarget>> Targets = SceneState.TargetManager->BuildAllSelectedTargetable(SceneState, GetTargetRequirements());
NewTool->SetTargets(MoveTemp(Targets));
NewTool->SetWorld(SceneState.World);
return NewTool;
}
/*
* Tool
*/
URemeshMeshToolProperties::URemeshMeshToolProperties()
{
TargetTriangleCount = 5000;
SmoothingStrength = 0.25;
RemeshIterations = 20;
MaxRemeshIterations = 20;
ExtraProjectionIterations = 5;
bDiscardAttributes = false;
RemeshType = ERemeshType::Standard;
SmoothingType = ERemeshSmoothingType::MeanValue;
bPreserveSharpEdges = true;
bShowGroupColors = false;
TargetEdgeLength = 5.0;
bFlips = true;
bSplits = true;
bCollapses = true;
bReproject = true;
bPreventNormalFlips = true;
bUseTargetEdgeLength = false;
}
URemeshMeshTool::URemeshMeshTool(const FObjectInitializer&)
{
BasicProperties = CreateDefaultSubobject<URemeshMeshToolProperties>(TEXT("RemeshProperties"));
// CreateDefaultSubobject automatically sets RF_Transactional flag, we need to clear it so that undo/redo doesn't affect tool properties
BasicProperties->ClearFlags(RF_Transactional);
}
void URemeshMeshTool::SetWorld(UWorld* World)
{
this->TargetWorld = World;
}
void URemeshMeshTool::Setup()
{
UInteractiveTool::Setup();
check(BasicProperties);
BasicProperties->RestoreProperties(this);
MeshStatisticsProperties = NewObject<UMeshStatisticsProperties>(this);
check(Targets.Num() > 0);
check(Targets[0]);
IPrimitiveComponentBackedTarget* TargetComponent = TargetComponentInterface(0);
IMeshDescriptionProvider* TargetMeshProvider = TargetMeshProviderInterface(0);
// hide component and create + show preview
TargetComponent->SetOwnerVisibility(false);
Preview = NewObject<UMeshOpPreviewWithBackgroundCompute>(this);
Preview->Setup(this->TargetWorld, this);
FComponentMaterialSet MaterialSet = UE::ToolTarget::GetMaterialSet(Targets[0]);
Preview->ConfigureMaterials( MaterialSet.Materials,
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager())
);
BasicProperties->WatchProperty(BasicProperties->bShowGroupColors,
[this](bool bNewValue) { UpdateVisualization();});
OriginalMesh = MakeShared<FDynamicMesh3, ESPMode::ThreadSafe>();
FMeshDescriptionToDynamicMesh Converter;
Converter.Convert(TargetMeshProvider->GetMeshDescription(), *OriginalMesh);
Preview->PreviewMesh->SetTransform(TargetComponent->GetWorldTransform());
Preview->PreviewMesh->SetTangentsMode(EDynamicMeshComponentTangentsMode::AutoCalculated);
Preview->PreviewMesh->UpdatePreview(OriginalMesh.Get());
OriginalMeshSpatial = MakeShared<FDynamicMeshAABBTree3, ESPMode::ThreadSafe>(OriginalMesh.Get(), true);
// calculate initial mesh area (no utility fn yet)
// TODO: will need to change to account for component transform's Scale3D
InitialMeshArea = 0;
for (int tid : OriginalMesh->TriangleIndicesItr())
{
InitialMeshArea += OriginalMesh->GetTriArea(tid);
}
// set properties defaults
// arbitrary threshold of 5000 tris seems reasonable?
BasicProperties->TargetTriangleCount = (OriginalMesh->TriangleCount() < 5000) ? 5000 : OriginalMesh->TriangleCount();
BasicProperties->TargetEdgeLength = CalculateTargetEdgeLength(BasicProperties->TargetTriangleCount);
// add properties to GUI
AddToolPropertySource(BasicProperties);
AddToolPropertySource(MeshStatisticsProperties);
MeshElementsDisplay = NewObject<UMeshElementsVisualizer>(this);
MeshElementsDisplay->CreateInWorld(Preview->PreviewMesh->GetWorld(), Preview->PreviewMesh->GetTransform());
if (ensure(MeshElementsDisplay->Settings))
{
MeshElementsDisplay->Settings->bShowWireframe = true;
MeshElementsDisplay->Settings->RestoreProperties(this, TEXT("Remesh"));
AddToolPropertySource(MeshElementsDisplay->Settings);
}
MeshElementsDisplay->SetMeshAccessFunction([this](UMeshElementsVisualizer::ProcessDynamicMeshFunc ProcessFunc) {
Preview->ProcessCurrentMesh(ProcessFunc);
});
Preview->OnMeshUpdated.AddLambda([this](UMeshOpPreviewWithBackgroundCompute* Compute)
{
Compute->ProcessCurrentMesh([&](const FDynamicMesh3& ReadMesh)
{
MeshStatisticsProperties->Update(ReadMesh);
MeshElementsDisplay->NotifyMeshChanged();
});
});
Preview->InvalidateResult();
SetToolDisplayName(LOCTEXT("ToolName", "Remesh"));
GetToolManager()->DisplayMessage(
LOCTEXT("OnStartTool", "Retriangulate the selected Mesh. Use the Boundary Constraints to preserve mesh borders. Enable Discard Attributes to ignore UV/Normal Seams. "),
EToolMessageLevel::UserNotification);
}
void URemeshMeshTool::Shutdown(EToolShutdownType ShutdownType)
{
BasicProperties->SaveProperties(this);
if (ensure(MeshElementsDisplay->Settings))
{
MeshElementsDisplay->Settings->SaveProperties(this, TEXT("Remesh"));
}
MeshElementsDisplay->Disconnect();
for (int ComponentIdx = 0; ComponentIdx < Targets.Num(); ComponentIdx++)
{
UE::ToolTarget::ShowSourceObject(Targets[ComponentIdx]);
}
FDynamicMeshOpResult Result = Preview->Shutdown();
if (ShutdownType == EToolShutdownType::Accept)
{
GetToolManager()->BeginUndoTransaction(LOCTEXT("RemeshMeshToolTransactionName", "Remesh Mesh"));
UE::ToolTarget::CommitDynamicMeshUpdate(Targets[0], *Result.Mesh, true);
GetToolManager()->EndUndoTransaction();
}
}
void URemeshMeshTool::OnTick(float DeltaTime)
{
Preview->Tick(DeltaTime);
MeshElementsDisplay->OnTick(DeltaTime);
}
TUniquePtr<FDynamicMeshOperator> URemeshMeshTool::MakeNewOperator()
{
TUniquePtr<FRemeshMeshOp> Op = MakeUnique<FRemeshMeshOp>();
Op->RemeshType = BasicProperties->RemeshType;
if (!BasicProperties->bUseTargetEdgeLength)
{
Op->TargetEdgeLength = CalculateTargetEdgeLength(BasicProperties->TargetTriangleCount);
Op->TriangleCountHint = 2.0 * BasicProperties->TargetTriangleCount;
}
else
{
Op->TargetEdgeLength = BasicProperties->TargetEdgeLength;
}
Op->bCollapses = BasicProperties->bCollapses;
Op->bDiscardAttributes = BasicProperties->bDiscardAttributes;
Op->bFlips = BasicProperties->bFlips;
Op->bPreserveSharpEdges = BasicProperties->bPreserveSharpEdges;
Op->MeshBoundaryConstraint = (EEdgeRefineFlags)BasicProperties->MeshBoundaryConstraint;
Op->GroupBoundaryConstraint = (EEdgeRefineFlags)BasicProperties->GroupBoundaryConstraint;
Op->MaterialBoundaryConstraint = (EEdgeRefineFlags)BasicProperties->MaterialBoundaryConstraint;
Op->bPreventNormalFlips = BasicProperties->bPreventNormalFlips;
Op->bReproject = BasicProperties->bReproject;
Op->bSplits = BasicProperties->bSplits;
Op->RemeshIterations = BasicProperties->RemeshIterations;
Op->MaxRemeshIterations = BasicProperties->MaxRemeshIterations;
Op->ExtraProjectionIterations = BasicProperties->ExtraProjectionIterations;
Op->SmoothingStrength = BasicProperties->SmoothingStrength;
Op->SmoothingType = BasicProperties->SmoothingType;
FTransform LocalToWorld = (FTransform)UE::ToolTarget::GetLocalToWorldTransform(Targets[0]);
Op->SetTransform(LocalToWorld);
Op->OriginalMesh = OriginalMesh;
Op->OriginalMeshSpatial = OriginalMeshSpatial;
Op->ProjectionTarget = nullptr;
Op->ProjectionTargetSpatial = nullptr;
return Op;
}
void URemeshMeshTool::OnPropertyModified(UObject* PropertySet, FProperty* Property)
{
if ( Property )
{
if ( Property->GetFName() != GET_MEMBER_NAME_CHECKED(URemeshMeshToolProperties, bShowGroupColors) )
{
Preview->InvalidateResult();
}
}
}
void URemeshMeshTool::UpdateVisualization()
{
FComponentMaterialSet MaterialSet;
if (BasicProperties->bShowGroupColors)
{
MaterialSet.Materials = {ToolSetupUtil::GetSelectionMaterial(GetToolManager())};
Preview->PreviewMesh->SetTriangleColorFunction([this](const FDynamicMesh3* Mesh, int TriangleID)
{
return LinearColors::SelectFColor(Mesh->GetTriangleGroup(TriangleID));
},
UPreviewMesh::ERenderUpdateMode::FastUpdate);
}
else
{
MaterialSet = UE::ToolTarget::GetMaterialSet(Targets[0]);
Preview->PreviewMesh->ClearTriangleColorFunction(UPreviewMesh::ERenderUpdateMode::FastUpdate);
}
Preview->ConfigureMaterials(MaterialSet.Materials,
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager()));
}
double URemeshMeshTool::CalculateTargetEdgeLength(int TargetTriCount)
{
double TargetTriArea = InitialMeshArea / (double)TargetTriCount;
double EdgeLen = TriangleUtil::EquilateralEdgeLengthForArea(TargetTriArea);
return (double)FMath::RoundToInt(EdgeLen*100.0) / 100.0;
}
bool URemeshMeshTool::CanAccept() const
{
return Super::CanAccept() && Preview->HaveValidResult();
}
#undef LOCTEXT_NAMESPACE