2019-12-27 09:26:59 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
#include "SimplifyMeshTool.h"
|
|
|
|
|
#include "InteractiveToolManager.h"
|
2020-02-14 17:00:18 -05:00
|
|
|
#include "Properties/RemeshProperties.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
#include "ToolBuilderUtil.h"
|
|
|
|
|
|
2019-10-17 20:26:57 -04:00
|
|
|
#include "ToolSetupUtil.h"
|
2020-01-27 20:11:15 -05:00
|
|
|
#include "Util/ColorConstants.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-17 20:26:57 -04:00
|
|
|
#include "DynamicMesh3.h"
|
|
|
|
|
|
|
|
|
|
#include "MeshDescriptionToDynamicMesh.h"
|
|
|
|
|
#include "DynamicMeshToMeshDescription.h"
|
|
|
|
|
|
|
|
|
|
#include "AssetGenerationUtil.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
#include "SceneManagement.h" // for FPrimitiveDrawInterface
|
|
|
|
|
|
|
|
|
|
//#include "ProfilingDebugging/ScopedTimers.h" // enable this to use the timer.
|
2019-10-17 20:26:57 -04:00
|
|
|
#include "Modules/ModuleManager.h"
|
|
|
|
|
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
#include "IMeshReductionManagerModule.h"
|
|
|
|
|
#include "IMeshReductionInterfaces.h"
|
2019-10-17 20:26:57 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
|
#include "Misc/ScopedSlowTask.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
#define LOCTEXT_NAMESPACE "USimplifyMeshTool"
|
|
|
|
|
|
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogMeshSimplification, Log, All);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ToolBuilder
|
|
|
|
|
*/
|
|
|
|
|
bool USimplifyMeshToolBuilder::CanBuildTool(const FToolBuilderState& SceneState) const
|
|
|
|
|
{
|
|
|
|
|
return ToolBuilderUtil::CountComponents(SceneState, CanMakeComponentTarget) == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UInteractiveTool* USimplifyMeshToolBuilder::BuildTool(const FToolBuilderState& SceneState) const
|
|
|
|
|
{
|
|
|
|
|
USimplifyMeshTool* NewTool = NewObject<USimplifyMeshTool>(SceneState.ToolManager);
|
|
|
|
|
|
|
|
|
|
UActorComponent* ActorComponent = ToolBuilderUtil::FindFirstComponent(SceneState, CanMakeComponentTarget);
|
|
|
|
|
auto* MeshComponent = Cast<UPrimitiveComponent>(ActorComponent);
|
|
|
|
|
check(MeshComponent != nullptr);
|
2019-10-17 20:26:57 -04:00
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
NewTool->SetSelection(MakeComponentTarget(MeshComponent));
|
2019-10-17 20:26:57 -04:00
|
|
|
NewTool->SetWorld(SceneState.World);
|
|
|
|
|
NewTool->SetAssetAPI(AssetAPI);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
return NewTool;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Tool
|
|
|
|
|
*/
|
|
|
|
|
USimplifyMeshToolProperties::USimplifyMeshToolProperties()
|
|
|
|
|
{
|
|
|
|
|
SimplifierType = ESimplifyType::QEM;
|
|
|
|
|
TargetMode = ESimplifyTargetType::Percentage;
|
|
|
|
|
TargetPercentage = 50;
|
|
|
|
|
TargetCount = 1000;
|
|
|
|
|
TargetEdgeLength = 5.0;
|
|
|
|
|
bReproject = false;
|
|
|
|
|
bPreventNormalFlips = true;
|
|
|
|
|
bDiscardAttributes = false;
|
2020-01-27 20:11:15 -05:00
|
|
|
bShowWireframe = true;
|
|
|
|
|
bShowGroupColors = false;
|
2020-02-14 17:00:18 -05:00
|
|
|
GroupBoundaryConstraint = EGroupBoundaryConstraint::Ignore;
|
|
|
|
|
MaterialBoundaryConstraint = EMaterialBoundaryConstraint::Ignore;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
2020-03-10 16:59:50 -04:00
|
|
|
void
|
|
|
|
|
USimplifyMeshToolProperties::SaveRestoreProperties(UInteractiveTool* RestoreToTool, bool bSaving)
|
|
|
|
|
{
|
|
|
|
|
USimplifyMeshToolProperties* PropertyCache = GetPropertyCache<USimplifyMeshToolProperties>();
|
|
|
|
|
|
|
|
|
|
// MeshConstraintProperties
|
|
|
|
|
SaveRestoreProperty(PropertyCache->bPreserveSharpEdges, this->bPreserveSharpEdges, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->MeshBoundaryConstraint, this->MeshBoundaryConstraint, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->GroupBoundaryConstraint, this->GroupBoundaryConstraint, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->MaterialBoundaryConstraint, this->MaterialBoundaryConstraint, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->bPreventNormalFlips, this->bPreventNormalFlips, bSaving);
|
|
|
|
|
|
|
|
|
|
// SimplifyMeshToolProperties
|
|
|
|
|
SaveRestoreProperty(PropertyCache->TargetMode, this->TargetMode, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->SimplifierType, this->SimplifierType, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->TargetPercentage, this->TargetPercentage, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->TargetEdgeLength, this->TargetEdgeLength, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->TargetCount, this->TargetCount, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->bDiscardAttributes, this->bDiscardAttributes, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->bShowWireframe, this->bShowWireframe, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->bShowGroupColors, this->bShowGroupColors, bSaving);
|
|
|
|
|
SaveRestoreProperty(PropertyCache->bReproject, this->bReproject, bSaving);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-17 20:26:57 -04:00
|
|
|
void USimplifyMeshTool::SetWorld(UWorld* World)
|
|
|
|
|
{
|
|
|
|
|
this->TargetWorld = World;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void USimplifyMeshTool::SetAssetAPI(IToolsContextAssetAPI* AssetAPIIn)
|
|
|
|
|
{
|
|
|
|
|
this->AssetAPI = AssetAPIIn;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
void USimplifyMeshTool::Setup()
|
|
|
|
|
{
|
|
|
|
|
UInteractiveTool::Setup();
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
|
2019-10-17 20:26:57 -04:00
|
|
|
// hide component and create + show preview
|
2019-10-01 20:41:42 -04:00
|
|
|
ComponentTarget->SetOwnerVisibility(false);
|
2019-10-17 20:26:57 -04:00
|
|
|
Preview = NewObject<UMeshOpPreviewWithBackgroundCompute>(this, "Preview");
|
|
|
|
|
Preview->Setup(this->TargetWorld, this);
|
2019-12-19 18:07:47 -05:00
|
|
|
FComponentMaterialSet MaterialSet;
|
|
|
|
|
ComponentTarget->GetMaterialSet(MaterialSet);
|
|
|
|
|
Preview->ConfigureMaterials( MaterialSet.Materials,
|
2019-10-17 20:26:57 -04:00
|
|
|
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager())
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// if in editor, create progress indicator dialog because building mesh copies can be slow (for very large meshes)
|
|
|
|
|
// this is especially needed because of the copy we make of the meshdescription; for Reasons, copying meshdescription is pretty slow
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
|
static const FText SlowTaskText = LOCTEXT("SimplifyMeshInit", "Building mesh simplification data...");
|
|
|
|
|
|
|
|
|
|
FScopedSlowTask SlowTask(3.0f, SlowTaskText);
|
|
|
|
|
SlowTask.MakeDialog();
|
|
|
|
|
|
|
|
|
|
// Declare progress shortcut lambdas
|
|
|
|
|
auto EnterProgressFrame = [&SlowTask](int Progress)
|
2020-01-07 15:54:23 -05:00
|
|
|
{
|
2019-10-17 20:26:57 -04:00
|
|
|
SlowTask.EnterProgressFrame((float)Progress);
|
|
|
|
|
};
|
|
|
|
|
#else
|
|
|
|
|
auto EnterProgressFrame = [](int Progress) {};
|
|
|
|
|
#endif
|
|
|
|
|
OriginalMeshDescription = MakeShared<FMeshDescription>(*ComponentTarget->GetMesh());
|
|
|
|
|
EnterProgressFrame(1);
|
|
|
|
|
OriginalMesh = MakeShared<FDynamicMesh3>();
|
|
|
|
|
FMeshDescriptionToDynamicMesh Converter;
|
|
|
|
|
Converter.Convert(ComponentTarget->GetMesh(), *OriginalMesh);
|
|
|
|
|
EnterProgressFrame(2);
|
|
|
|
|
OriginalMeshSpatial = MakeShared<FDynamicMeshAABBTree3>(OriginalMesh.Get(), true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Preview->PreviewMesh->SetTransform(ComponentTarget->GetWorldTransform());
|
2020-03-07 10:52:46 -05:00
|
|
|
Preview->PreviewMesh->SetTangentsMode(EDynamicMeshTangentCalcType::AutoCalculated);
|
2019-10-17 20:26:57 -04:00
|
|
|
Preview->PreviewMesh->UpdatePreview(OriginalMesh.Get());
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
// initialize our properties
|
|
|
|
|
SimplifyProperties = NewObject<USimplifyMeshToolProperties>(this);
|
2020-03-10 16:59:50 -04:00
|
|
|
SimplifyProperties->RestoreProperties(this);
|
2019-10-01 20:41:42 -04:00
|
|
|
AddToolPropertySource(SimplifyProperties);
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
ShowGroupsWatcher.Initialize(
|
|
|
|
|
[this]() { return SimplifyProperties->bShowGroupColors; },
|
|
|
|
|
[this](bool bNewValue) { UpdateVisualization(); }, SimplifyProperties->bShowGroupColors );
|
|
|
|
|
ShowWireFrameWatcher.Initialize(
|
|
|
|
|
[this]() { return SimplifyProperties->bShowWireframe; },
|
|
|
|
|
[this](bool bNewValue) { UpdateVisualization(); }, SimplifyProperties->bShowWireframe );
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
MeshStatisticsProperties = NewObject<UMeshStatisticsProperties>(this);
|
|
|
|
|
AddToolPropertySource(MeshStatisticsProperties);
|
|
|
|
|
|
2019-10-17 20:26:57 -04:00
|
|
|
Preview->OnMeshUpdated.AddLambda([this](UMeshOpPreviewWithBackgroundCompute* Compute)
|
|
|
|
|
{
|
|
|
|
|
MeshStatisticsProperties->Update(*Compute->PreviewMesh->GetPreviewDynamicMesh());
|
|
|
|
|
});
|
|
|
|
|
|
2020-03-06 16:56:32 -05:00
|
|
|
UpdateVisualization();
|
2019-10-17 20:26:57 -04:00
|
|
|
Preview->InvalidateResult();
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void USimplifyMeshTool::Shutdown(EToolShutdownType ShutdownType)
|
|
|
|
|
{
|
2020-03-10 16:59:50 -04:00
|
|
|
SimplifyProperties->SaveProperties(this);
|
|
|
|
|
ComponentTarget->SetOwnerVisibility(true);
|
2019-11-01 17:39:56 -04:00
|
|
|
FDynamicMeshOpResult Result = Preview->Shutdown();
|
2020-03-10 16:59:50 -04:00
|
|
|
if (ShutdownType == EToolShutdownType::Accept)
|
|
|
|
|
{
|
2019-11-01 17:39:56 -04:00
|
|
|
GenerateAsset(Result);
|
2020-03-10 16:59:50 -04:00
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-10-17 20:26:57 -04:00
|
|
|
void USimplifyMeshTool::Tick(float DeltaTime)
|
|
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
ShowWireFrameWatcher.CheckAndUpdate();
|
|
|
|
|
ShowGroupsWatcher.CheckAndUpdate();
|
|
|
|
|
|
2019-10-17 20:26:57 -04:00
|
|
|
Preview->Tick(DeltaTime);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 17:39:56 -04:00
|
|
|
TUniquePtr<FDynamicMeshOperator> USimplifyMeshTool::MakeNewOperator()
|
2019-10-17 20:26:57 -04:00
|
|
|
{
|
2019-11-01 17:39:56 -04:00
|
|
|
TUniquePtr<FSimplifyMeshOp> Op = MakeUnique<FSimplifyMeshOp>();
|
2019-10-17 20:26:57 -04:00
|
|
|
|
|
|
|
|
Op->bDiscardAttributes = SimplifyProperties->bDiscardAttributes;
|
|
|
|
|
Op->bPreventNormalFlips = SimplifyProperties->bPreventNormalFlips;
|
2020-01-27 20:11:15 -05:00
|
|
|
Op->bPreserveSharpEdges = SimplifyProperties->bPreserveSharpEdges;
|
2019-10-17 20:26:57 -04:00
|
|
|
Op->bReproject = SimplifyProperties->bReproject;
|
|
|
|
|
Op->SimplifierType = SimplifyProperties->SimplifierType;
|
|
|
|
|
Op->TargetCount = SimplifyProperties->TargetCount;
|
|
|
|
|
Op->TargetEdgeLength = SimplifyProperties->TargetEdgeLength;
|
|
|
|
|
Op->TargetMode = SimplifyProperties->TargetMode;
|
|
|
|
|
Op->TargetPercentage = SimplifyProperties->TargetPercentage;
|
2020-01-27 20:11:15 -05:00
|
|
|
Op->MeshBoundaryConstraint = (EEdgeRefineFlags)SimplifyProperties->MeshBoundaryConstraint;
|
|
|
|
|
Op->GroupBoundaryConstraint = (EEdgeRefineFlags)SimplifyProperties->GroupBoundaryConstraint;
|
|
|
|
|
Op->MaterialBoundaryConstraint = (EEdgeRefineFlags)SimplifyProperties->MaterialBoundaryConstraint;
|
2019-10-17 20:26:57 -04:00
|
|
|
FTransform LocalToWorld = ComponentTarget->GetWorldTransform();
|
|
|
|
|
Op->SetTransform(LocalToWorld);
|
|
|
|
|
|
|
|
|
|
Op->OriginalMeshDescription = OriginalMeshDescription;
|
|
|
|
|
Op->OriginalMesh = OriginalMesh;
|
|
|
|
|
Op->OriginalMeshSpatial = OriginalMeshSpatial;
|
|
|
|
|
|
|
|
|
|
IMeshReductionManagerModule& MeshReductionModule = FModuleManager::Get().LoadModuleChecked<IMeshReductionManagerModule>("MeshReductionInterface");
|
|
|
|
|
Op->MeshReduction = MeshReductionModule.GetStaticMeshReductionInterface();
|
|
|
|
|
|
|
|
|
|
return Op;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
void USimplifyMeshTool::Render(IToolsContextRenderAPI* RenderAPI)
|
|
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
FPrimitiveDrawInterface* PDI = RenderAPI->GetPrimitiveDrawInterface();
|
|
|
|
|
FTransform Transform = ComponentTarget->GetWorldTransform(); //Actor->GetTransform();
|
|
|
|
|
|
|
|
|
|
FColor LineColor(255, 0, 0);
|
2019-10-17 20:26:57 -04:00
|
|
|
const FDynamicMesh3* TargetMesh = Preview->PreviewMesh->GetPreviewDynamicMesh();
|
2019-10-01 20:41:42 -04:00
|
|
|
if (TargetMesh->HasAttributes())
|
|
|
|
|
{
|
2019-10-17 20:26:57 -04:00
|
|
|
const FDynamicMeshUVOverlay* UVOverlay = TargetMesh->Attributes()->PrimaryUV();
|
2020-01-27 20:11:15 -05:00
|
|
|
for (int eid : TargetMesh->EdgeIndicesItr())
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
if (UVOverlay->IsSeamEdge(eid))
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
FVector3d A, B;
|
|
|
|
|
TargetMesh->GetEdgeV(eid, A, B);
|
2019-12-19 18:07:47 -05:00
|
|
|
PDI->DrawLine(Transform.TransformPosition((FVector)A), Transform.TransformPosition((FVector)B),
|
2019-10-01 20:41:42 -04:00
|
|
|
LineColor, 0, 2.0, 1.0f, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 15:54:23 -05:00
|
|
|
void USimplifyMeshTool::OnPropertyModified(UObject* PropertySet, FProperty* Property)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2020-03-06 16:56:32 -05:00
|
|
|
if ( Property )
|
|
|
|
|
{
|
|
|
|
|
if ( ( Property->GetFName() == GET_MEMBER_NAME_CHECKED(USimplifyMeshToolProperties, bShowWireframe) ) ||
|
|
|
|
|
( Property->GetFName() == GET_MEMBER_NAME_CHECKED(USimplifyMeshToolProperties, bShowGroupColors) ) )
|
|
|
|
|
{
|
|
|
|
|
UpdateVisualization();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Preview->InvalidateResult();
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
void USimplifyMeshTool::UpdateVisualization()
|
|
|
|
|
{
|
|
|
|
|
Preview->PreviewMesh->EnableWireframe(SimplifyProperties->bShowWireframe);
|
|
|
|
|
FComponentMaterialSet MaterialSet;
|
|
|
|
|
if (SimplifyProperties->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
|
|
|
|
|
{
|
|
|
|
|
ComponentTarget->GetMaterialSet(MaterialSet);
|
|
|
|
|
Preview->PreviewMesh->ClearTriangleColorFunction(UPreviewMesh::ERenderUpdateMode::FastUpdate);
|
|
|
|
|
}
|
|
|
|
|
Preview->ConfigureMaterials(MaterialSet.Materials,
|
|
|
|
|
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager()));
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
bool USimplifyMeshTool::HasAccept() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool USimplifyMeshTool::CanAccept() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-17 20:26:57 -04:00
|
|
|
void USimplifyMeshTool::GenerateAsset(const FDynamicMeshOpResult& Result)
|
|
|
|
|
{
|
|
|
|
|
GetToolManager()->BeginUndoTransaction(LOCTEXT("SimplifyMeshToolTransactionName", "Simplify Mesh"));
|
|
|
|
|
|
|
|
|
|
check(Result.Mesh.Get() != nullptr);
|
2019-11-14 15:31:53 -05:00
|
|
|
ComponentTarget->CommitMesh([&Result](const FPrimitiveComponentTarget::FCommitParams& CommitParams)
|
2019-10-17 20:26:57 -04:00
|
|
|
{
|
|
|
|
|
FDynamicMeshToMeshDescription Converter;
|
|
|
|
|
|
|
|
|
|
// full conversion if normal topology changed or faces were inverted
|
2019-11-14 15:31:53 -05:00
|
|
|
Converter.Convert(Result.Mesh.Get(), *CommitParams.MeshDescription);
|
2019-10-17 20:26:57 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
GetToolManager()->EndUndoTransaction();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|