Files
UnrealEngineUWP/Engine/Plugins/Experimental/MeshModelingToolset/Source/MeshModelingTools/Private/ConvertToPolygonsTool.cpp
Ryan Schmidt 1a178fa774 ModelingTools: clean up old PDI-based mesh edge rendering in Weld, Generate Polygroups, Remesh, Simplify. Replace with usage of UMeshElementsVisualizer and/or UPreviewGeometry. Also clean up includes and port to UE::ToolTarget:: APIs.
UMeshElementsVisualizer::SetMeshAccessFunction() now takes a TFunction with a TFunctionRef argument, instead of with a FDynamicMesh3* argument. This allows the UMeshElementsVisualizer to access the target mesh via the ProcessMesh()-style call on a UPreviewMesh, UDynamicMesh, etc, rather than receiving direct pointer access. See UWeldMeshEdgesTool::Setup() for example usage.

Add UMeshOpPreviewWithBackgroundCompute::ProcessCurrentMesh(), which forwards to the embedded UPreviewMesh::ProcessMesh()  (which can be used with the above)

Add UE::ToolTarget::GetMeshDescriptionCopy() to ModelingToolTargetUtil, similar to existing DynamicMesh variant that will auto-compute tangents if necessary.

#rb none
#rnx
#jira none
#preflight 60c8def586ce760001d8de51
#fyi semion.piskarev

[CL 16679472 by Ryan Schmidt in ue5-main branch]
2021-06-15 17:05:25 -04:00

317 lines
9.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ConvertToPolygonsTool.h"
#include "InteractiveToolManager.h"
#include "ToolBuilderUtil.h"
#include "ToolSetupUtil.h"
#include "ModelingToolTargetUtil.h"
#include "Drawing/PreviewGeometryActor.h"
#include "PreviewMesh.h"
#include "MeshOpPreviewHelpers.h"
#include "ModelingOperators.h"
#include "DynamicMesh/DynamicMesh3.h"
#include "DynamicMesh/MeshNormals.h"
#include "DynamicMeshEditor.h"
#include "MeshRegionBoundaryLoops.h"
#include "Util/ColorConstants.h"
#include "Polygroups/PolygroupUtil.h"
#include "Util/ColorConstants.h"
#include "FindPolygonsAlgorithm.h"
#include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types)
using namespace UE::Geometry;
#define LOCTEXT_NAMESPACE "UConvertToPolygonsTool"
/*
* ToolBuilder
*/
USingleSelectionMeshEditingTool* UConvertToPolygonsToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const
{
return NewObject<UConvertToPolygonsTool>(SceneState.ToolManager);
}
class FConvertToPolygonsOp : public FDynamicMeshOperator
{
public:
FConvertToPolygonsOp() : FDynamicMeshOperator() {}
virtual void CalculateResult(FProgressCancel* Progress) override
{
if ((Progress && Progress->Cancelled()) || !OriginalMesh)
{
return;
}
ResultMesh->Copy(*OriginalMesh, true, true, true, true);
if (Progress && Progress->Cancelled())
{
return;
}
Polygons = FFindPolygonsAlgorithm(ResultMesh.Get());
switch (ConversionMode)
{
case EConvertToPolygonsMode::FromUVIslands:
{
Polygons.FindPolygonsFromUVIslands();
break;
}
case EConvertToPolygonsMode::FromConnectedTris:
{
Polygons.FindPolygonsFromConnectedTris();
break;
}
case EConvertToPolygonsMode::FaceNormalDeviation:
{
double DotTolerance = 1.0 - FMathd::Cos(AngleTolerance * FMathd::DegToRad);
Polygons.FindPolygonsFromFaceNormals(DotTolerance);
break;
}
default:
check(0);
}
Polygons.FindPolygonEdges();
if (bCalculateNormals && ConversionMode == EConvertToPolygonsMode::FaceNormalDeviation)
{
if (ResultMesh->HasAttributes() == false)
{
ResultMesh->EnableAttributes();
}
FDynamicMeshNormalOverlay* NormalOverlay = ResultMesh->Attributes()->PrimaryNormals();
NormalOverlay->ClearElements();
FDynamicMeshEditor Editor(ResultMesh.Get());
for (const TArray<int>& Polygon : Polygons.FoundPolygons)
{
FVector3f Normal = (FVector3f)ResultMesh->GetTriNormal(Polygon[0]);
Editor.SetTriangleNormals(Polygon, Normal);
}
FMeshNormals Normals(ResultMesh.Get());
Normals.RecomputeOverlayNormals(ResultMesh->Attributes()->PrimaryNormals());
Normals.CopyToOverlay(NormalOverlay, false);
}
}
void SetTransform(const FTransform& Transform)
{
ResultTransform = (UE::Geometry::FTransform3d)Transform;
}
FFindPolygonsAlgorithm Polygons;
TSharedPtr<FDynamicMesh3, ESPMode::ThreadSafe> OriginalMesh;
// parameters set by the tool
EConvertToPolygonsMode ConversionMode;
double AngleTolerance;
bool bCalculateNormals;
};
TUniquePtr<FDynamicMeshOperator> UConvertToPolygonsOperatorFactory::MakeNewOperator()
{
// backpointer used to populate parameters.
check(ConvertToPolygonsTool);
// Create the actual operator type based on the requested operation
TUniquePtr<FConvertToPolygonsOp> MeshOp = MakeUnique<FConvertToPolygonsOp>();
// Operator runs on another thread - copy data over that it needs.
ConvertToPolygonsTool->UpdateOpParameters(*MeshOp);
// give the operator
return MeshOp;
}
/*
* Tool
*/
UConvertToPolygonsTool::UConvertToPolygonsTool()
{
SetToolDisplayName(LOCTEXT("ConvertToPolygonsToolName", "Generate PolyGroups"));
}
bool UConvertToPolygonsTool::CanAccept() const
{
return Super::CanAccept() && (PreviewCompute == nullptr || PreviewCompute->HaveValidResult());
}
void UConvertToPolygonsTool::Setup()
{
UInteractiveTool::Setup();
FComponentMaterialSet MaterialSet = UE::ToolTarget::GetMaterialSet(Target);
OriginalDynamicMesh = MakeShared<FDynamicMesh3, ESPMode::ThreadSafe>(UE::ToolTarget::GetDynamicMeshCopy(Target));
Settings = NewObject<UConvertToPolygonsToolProperties>(this);
Settings->RestoreProperties(this);
AddToolPropertySource(Settings);
FTransform MeshTransform = (FTransform)UE::ToolTarget::GetLocalToWorldTransform(Target);
UE::ToolTarget::HideSourceObject(Target);
{
// create the operator factory
UConvertToPolygonsOperatorFactory* ConvertToPolygonsOperatorFactory = NewObject<UConvertToPolygonsOperatorFactory>(this);
ConvertToPolygonsOperatorFactory->ConvertToPolygonsTool = this; // set the back pointer
PreviewCompute = NewObject<UMeshOpPreviewWithBackgroundCompute>(ConvertToPolygonsOperatorFactory);
PreviewCompute->Setup(this->TargetWorld, ConvertToPolygonsOperatorFactory);
PreviewCompute->SetIsMeshTopologyConstant(true, EMeshRenderAttributeFlags::Positions | EMeshRenderAttributeFlags::VertexNormals);
// Give the preview something to display
PreviewCompute->PreviewMesh->SetTransform(MeshTransform);
PreviewCompute->PreviewMesh->SetTangentsMode(EDynamicMeshComponentTangentsMode::AutoCalculated);
PreviewCompute->PreviewMesh->UpdatePreview(OriginalDynamicMesh.Get());
PreviewCompute->ConfigureMaterials(MaterialSet.Materials,
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager())
);
// show the preview mesh
PreviewCompute->SetVisibility(true);
// something to capture the polygons from the async task when it is done
PreviewCompute->OnOpCompleted.AddLambda([this](const FDynamicMeshOperator* MeshOp)
{
const FConvertToPolygonsOp* ConvertToPolygonsOp = static_cast<const FConvertToPolygonsOp*>(MeshOp);
this->PolygonEdges = ConvertToPolygonsOp->Polygons.PolygonEdges;
UpdateVisualization();
});
// start the compute
PreviewCompute->InvalidateResult();
}
PreviewGeometry = NewObject<UPreviewGeometry>(this);
PreviewGeometry->CreateInWorld(TargetWorld, MeshTransform);
// updates the triangle color visualization
UpdateVisualization();
Settings->WatchProperty(Settings->ConversionMode,
[this](EConvertToPolygonsMode NewMode)
{ OnSettingsModified(); });
Settings->WatchProperty(Settings->bShowGroupColors,
[this](bool bNewValue)
{ UpdateVisualization(); });
Settings->WatchProperty(Settings->AngleTolerance,
[this](float value)
{ OnSettingsModified(); });
GetToolManager()->DisplayMessage(
LOCTEXT("OnStartTool", "Cluster triangles of the Mesh into PolyGroups using various strategies"),
EToolMessageLevel::UserNotification);
}
void UConvertToPolygonsTool::UpdateOpParameters(FConvertToPolygonsOp& ConvertToPolygonsOp) const
{
ConvertToPolygonsOp.bCalculateNormals = Settings->bCalculateNormals;
ConvertToPolygonsOp.ConversionMode = Settings->ConversionMode;
ConvertToPolygonsOp.AngleTolerance = Settings->AngleTolerance;
ConvertToPolygonsOp.OriginalMesh = OriginalDynamicMesh;
FTransform LocalToWorld = (FTransform)UE::ToolTarget::GetLocalToWorldTransform(Target);
ConvertToPolygonsOp.SetTransform(LocalToWorld);
}
void UConvertToPolygonsTool::Shutdown(EToolShutdownType ShutdownType)
{
Settings->SaveProperties(this);
UE::ToolTarget::ShowSourceObject(Target);
PreviewGeometry->Disconnect();
PreviewGeometry = nullptr;
if (PreviewCompute)
{
FDynamicMeshOpResult Result = PreviewCompute->Shutdown();
if (ShutdownType == EToolShutdownType::Accept)
{
GetToolManager()->BeginUndoTransaction(LOCTEXT("ConvertToPolygonsToolTransactionName", "Find Polygroups"));
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();
}
}
}
void UConvertToPolygonsTool::OnSettingsModified()
{
PreviewCompute->InvalidateResult();
}
void UConvertToPolygonsTool::OnTick(float DeltaTime)
{
PreviewCompute->Tick(DeltaTime);
}
void UConvertToPolygonsTool::UpdateVisualization()
{
if (!PreviewCompute)
{
return;
}
IMaterialProvider* MaterialTarget = Cast<IMaterialProvider>(Target);
FComponentMaterialSet MaterialSet;
if (Settings->bShowGroupColors)
{
int32 NumMaterials = MaterialTarget->GetNumMaterials();
for (int32 i = 0; i < NumMaterials; ++i)
{
MaterialSet.Materials.Add(ToolSetupUtil::GetSelectionMaterial(GetToolManager()));
}
PreviewCompute->PreviewMesh->SetTriangleColorFunction([this](const FDynamicMesh3* Mesh, int TriangleID)
{
return LinearColors::SelectFColor(Mesh->GetTriangleGroup(TriangleID));
},
UPreviewMesh::ERenderUpdateMode::FastUpdate);
}
else
{
MaterialTarget->GetMaterialSet(MaterialSet);
PreviewCompute->PreviewMesh->ClearTriangleColorFunction(UPreviewMesh::ERenderUpdateMode::FastUpdate);
}
PreviewCompute->ConfigureMaterials(MaterialSet.Materials,
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager()));
FColor GroupLineColor = FColor::Red;
float GroupLineThickness = 2.0f;
PreviewGeometry->CreateOrUpdateLineSet(TEXT("GroupBorders"), PolygonEdges.Num(),
[&](int32 k, TArray<FRenderableLine>& LinesOut) {
FVector3d A, B;
OriginalDynamicMesh->GetEdgeV(PolygonEdges[k], A, B);
LinesOut.Add(FRenderableLine(A, B, GroupLineColor, GroupLineThickness));
}, 1);
}
#undef LOCTEXT_NAMESPACE