2019-12-27 09:26:59 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
#include "EditNormalsTool.h"
|
|
|
|
|
#include "InteractiveToolManager.h"
|
|
|
|
|
#include "ToolBuilderUtil.h"
|
|
|
|
|
|
|
|
|
|
#include "ToolSetupUtil.h"
|
2021-11-23 09:42:40 -05:00
|
|
|
#include "ModelingToolTargetUtil.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2021-06-13 00:36:02 -04:00
|
|
|
#include "DynamicMesh/DynamicMesh3.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
#include "BaseBehaviors/MultiClickSequenceInputBehavior.h"
|
|
|
|
|
#include "Selection/SelectClickedAction.h"
|
|
|
|
|
|
|
|
|
|
#include "MeshDescriptionToDynamicMesh.h"
|
|
|
|
|
#include "DynamicMeshToMeshDescription.h"
|
|
|
|
|
|
|
|
|
|
#include "InteractiveGizmoManager.h"
|
|
|
|
|
|
2020-06-23 18:40:00 -04:00
|
|
|
#include "AssetUtils/MeshDescriptionUtil.h"
|
2021-12-06 12:42:19 -05:00
|
|
|
#include "Engine/StaticMesh.h"
|
|
|
|
|
#include "Components/StaticMeshComponent.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2021-03-11 11:40:03 -04:00
|
|
|
#include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
|
2021-12-06 12:42:19 -05:00
|
|
|
#include "ModelingToolTargetUtil.h"
|
2021-03-11 11:40:03 -04:00
|
|
|
|
2021-03-09 19:33:56 -04:00
|
|
|
using namespace UE::Geometry;
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "UEditNormalsTool"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ToolBuilder
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2021-11-23 09:42:40 -05:00
|
|
|
UMultiSelectionMeshEditingTool* UEditNormalsToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const
|
2021-03-11 11:40:03 -04:00
|
|
|
{
|
2021-11-23 09:42:40 -05:00
|
|
|
return NewObject<UEditNormalsTool>(SceneState.ToolManager);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Tool
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
UEditNormalsToolProperties::UEditNormalsToolProperties()
|
|
|
|
|
{
|
|
|
|
|
bFixInconsistentNormals = false;
|
|
|
|
|
bInvertNormals = false;
|
|
|
|
|
bRecomputeNormals = true;
|
|
|
|
|
NormalCalculationMethod = ENormalCalculationMethod::AreaAngleWeighting;
|
2019-12-19 18:07:47 -05:00
|
|
|
SplitNormalMethod = ESplitNormalMethod::UseExistingTopology;
|
2019-10-01 20:41:42 -04:00
|
|
|
SharpEdgeAngleThreshold = 60;
|
|
|
|
|
bAllowSharpVertices = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UEditNormalsAdvancedProperties::UEditNormalsAdvancedProperties()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UEditNormalsTool::UEditNormalsTool()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UEditNormalsTool::Setup()
|
|
|
|
|
{
|
|
|
|
|
UInteractiveTool::Setup();
|
|
|
|
|
|
|
|
|
|
// hide input StaticMeshComponent
|
2021-03-11 11:40:03 -04:00
|
|
|
for (int32 ComponentIdx = 0, NumTargets = Targets.Num(); ComponentIdx < NumTargets; ComponentIdx++)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2021-11-23 09:42:40 -05:00
|
|
|
UE::ToolTarget::HideSourceObject(Targets[ComponentIdx]);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BasicProperties = NewObject<UEditNormalsToolProperties>(this, TEXT("Mesh Normals Settings"));
|
2020-10-29 13:38:15 -04:00
|
|
|
BasicProperties->RestoreProperties(this);
|
2019-10-01 20:41:42 -04:00
|
|
|
AddToolPropertySource(BasicProperties);
|
2020-10-29 13:38:15 -04:00
|
|
|
|
|
|
|
|
AdvancedProperties = NewObject<UEditNormalsAdvancedProperties>(this, TEXT("Advanced Settings"));
|
|
|
|
|
AdvancedProperties->RestoreProperties(this);
|
2019-10-01 20:41:42 -04:00
|
|
|
AddToolPropertySource(AdvancedProperties);
|
|
|
|
|
|
|
|
|
|
// initialize the PreviewMesh+BackgroundCompute object
|
|
|
|
|
UpdateNumPreviews();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
|
|
|
{
|
|
|
|
|
Preview->InvalidateResult();
|
|
|
|
|
}
|
2020-09-24 00:43:27 -04:00
|
|
|
|
2021-02-08 17:02:09 -04:00
|
|
|
SetToolDisplayName(LOCTEXT("ToolName", "Edit Normals"));
|
2020-09-24 00:43:27 -04:00
|
|
|
GetToolManager()->DisplayMessage(
|
|
|
|
|
LOCTEXT("OnStartTool", "Configure or Recalculate Normals on a Mesh (disables autogenerated Normals)"),
|
|
|
|
|
EToolMessageLevel::UserNotification);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UEditNormalsTool::UpdateNumPreviews()
|
|
|
|
|
{
|
|
|
|
|
int32 CurrentNumPreview = Previews.Num();
|
2021-03-11 11:40:03 -04:00
|
|
|
int32 TargetNumPreview = Targets.Num();
|
2019-10-01 20:41:42 -04:00
|
|
|
if (TargetNumPreview < CurrentNumPreview)
|
|
|
|
|
{
|
|
|
|
|
for (int32 PreviewIdx = CurrentNumPreview - 1; PreviewIdx >= TargetNumPreview; PreviewIdx--)
|
|
|
|
|
{
|
|
|
|
|
Previews[PreviewIdx]->Cancel();
|
|
|
|
|
}
|
|
|
|
|
Previews.SetNum(TargetNumPreview);
|
|
|
|
|
OriginalDynamicMeshes.SetNum(TargetNumPreview);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
OriginalDynamicMeshes.SetNum(TargetNumPreview);
|
|
|
|
|
for (int32 PreviewIdx = CurrentNumPreview; PreviewIdx < TargetNumPreview; PreviewIdx++)
|
|
|
|
|
{
|
|
|
|
|
UEditNormalsOperatorFactory *OpFactory = NewObject<UEditNormalsOperatorFactory>();
|
|
|
|
|
OpFactory->Tool = this;
|
|
|
|
|
OpFactory->ComponentIndex = PreviewIdx;
|
2021-02-17 11:50:23 -04:00
|
|
|
OriginalDynamicMeshes[PreviewIdx] = MakeShared<FDynamicMesh3, ESPMode::ThreadSafe>();
|
2019-10-01 20:41:42 -04:00
|
|
|
FMeshDescriptionToDynamicMesh Converter;
|
2021-11-23 09:42:40 -05:00
|
|
|
Converter.Convert(UE::ToolTarget::GetMeshDescription(Targets[PreviewIdx]), *OriginalDynamicMeshes[PreviewIdx]);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
UMeshOpPreviewWithBackgroundCompute* Preview = Previews.Add_GetRef(NewObject<UMeshOpPreviewWithBackgroundCompute>(OpFactory, "Preview"));
|
2022-01-28 10:18:10 -05:00
|
|
|
Preview->Setup(GetTargetWorld(), OpFactory);
|
2021-10-07 22:25:54 -04:00
|
|
|
ToolSetupUtil::ApplyRenderingConfigurationToPreview(Preview->PreviewMesh, Targets[PreviewIdx]);
|
2021-06-11 22:42:32 -04:00
|
|
|
Preview->PreviewMesh->SetTangentsMode(EDynamicMeshComponentTangentsMode::AutoCalculated);
|
2019-12-19 18:07:47 -05:00
|
|
|
|
2021-11-23 09:42:40 -05:00
|
|
|
const FComponentMaterialSet MaterialSet = UE::ToolTarget::GetMaterialSet(Targets[PreviewIdx]);
|
2019-12-19 18:07:47 -05:00
|
|
|
Preview->ConfigureMaterials(MaterialSet.Materials,
|
2019-10-01 20:41:42 -04:00
|
|
|
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager())
|
|
|
|
|
);
|
2019-12-19 18:07:47 -05:00
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
Preview->SetVisibility(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-01-28 18:40:54 -05:00
|
|
|
void UEditNormalsTool::OnShutdown(EToolShutdownType ShutdownType)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2020-10-29 13:38:15 -04:00
|
|
|
BasicProperties->SaveProperties(this);
|
|
|
|
|
AdvancedProperties->SaveProperties(this);
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
// Restore (unhide) the source meshes
|
2021-03-11 11:40:03 -04:00
|
|
|
for (int32 ComponentIdx = 0, NumTargets = Targets.Num(); ComponentIdx < NumTargets; ComponentIdx++)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2021-11-23 09:42:40 -05:00
|
|
|
UE::ToolTarget::ShowSourceObject(Targets[ComponentIdx]);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
2019-10-02 12:05:44 -04:00
|
|
|
TArray<FDynamicMeshOpResult> Results;
|
2019-10-01 20:41:42 -04:00
|
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
|
|
|
{
|
2019-10-02 12:05:44 -04:00
|
|
|
Results.Add(Preview->Shutdown());
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
if (ShutdownType == EToolShutdownType::Accept)
|
|
|
|
|
{
|
|
|
|
|
GenerateAsset(Results);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-04 14:13:21 -04:00
|
|
|
TUniquePtr<FDynamicMeshOperator> UEditNormalsOperatorFactory::MakeNewOperator()
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-04 14:13:21 -04:00
|
|
|
TUniquePtr<FEditNormalsOp> NormalsOp = MakeUnique<FEditNormalsOp>();
|
2019-10-01 20:41:42 -04:00
|
|
|
NormalsOp->bFixInconsistentNormals = Tool->BasicProperties->bFixInconsistentNormals;
|
|
|
|
|
NormalsOp->bInvertNormals = Tool->BasicProperties->bInvertNormals;
|
|
|
|
|
NormalsOp->bRecomputeNormals = Tool->BasicProperties->bRecomputeNormals;
|
2019-12-19 18:07:47 -05:00
|
|
|
NormalsOp->SplitNormalMethod = Tool->BasicProperties->SplitNormalMethod;
|
2019-10-01 20:41:42 -04:00
|
|
|
NormalsOp->bAllowSharpVertices = Tool->BasicProperties->bAllowSharpVertices;
|
|
|
|
|
NormalsOp->NormalCalculationMethod = Tool->BasicProperties->NormalCalculationMethod;
|
|
|
|
|
NormalsOp->NormalSplitThreshold = Tool->BasicProperties->SharpEdgeAngleThreshold;
|
|
|
|
|
|
2021-11-23 09:42:40 -05:00
|
|
|
const FTransform LocalToWorld = (FTransform) UE::ToolTarget::GetLocalToWorldTransform(Tool->Targets[ComponentIndex]);
|
2019-10-01 20:41:42 -04:00
|
|
|
NormalsOp->OriginalMesh = Tool->OriginalDynamicMeshes[ComponentIndex];
|
|
|
|
|
|
|
|
|
|
NormalsOp->SetTransform(LocalToWorld);
|
|
|
|
|
|
|
|
|
|
return NormalsOp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UEditNormalsTool::Render(IToolsContextRenderAPI* RenderAPI)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-18 18:42:59 -04:00
|
|
|
void UEditNormalsTool::OnTick(float DeltaTime)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
|
|
|
{
|
|
|
|
|
Preview->Tick(DeltaTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
|
void UEditNormalsTool::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
|
|
|
{
|
|
|
|
|
UpdateNumPreviews();
|
|
|
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
|
|
|
{
|
|
|
|
|
Preview->InvalidateResult();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-01-07 15:54:23 -05:00
|
|
|
void UEditNormalsTool::OnPropertyModified(UObject* PropertySet, FProperty* Property)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
UpdateNumPreviews();
|
|
|
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
|
|
|
{
|
|
|
|
|
Preview->InvalidateResult();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool UEditNormalsTool::HasAccept() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UEditNormalsTool::CanAccept() const
|
|
|
|
|
{
|
|
|
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
|
|
|
{
|
|
|
|
|
if (!Preview->HaveValidResult())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-24 18:42:39 -04:00
|
|
|
return Super::CanAccept();
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-10-02 12:05:44 -04:00
|
|
|
void UEditNormalsTool::GenerateAsset(const TArray<FDynamicMeshOpResult>& Results)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
GetToolManager()->BeginUndoTransaction(LOCTEXT("EditNormalsToolTransactionName", "Edit Normals Tool"));
|
|
|
|
|
|
2021-03-11 11:40:03 -04:00
|
|
|
check(Results.Num() == Targets.Num());
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2021-03-11 11:40:03 -04:00
|
|
|
for (int32 ComponentIdx = 0; ComponentIdx < Targets.Num(); ComponentIdx++)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2020-06-23 18:40:00 -04:00
|
|
|
// disable auto-generated normals StaticMesh build setting
|
2021-11-23 09:42:40 -05:00
|
|
|
UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(UE::ToolTarget::GetTargetComponent(Targets[ComponentIdx]));
|
2020-06-23 18:40:00 -04:00
|
|
|
if (StaticMeshComponent != nullptr)
|
|
|
|
|
{
|
|
|
|
|
UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh();
|
|
|
|
|
if (ensure(StaticMesh != nullptr))
|
|
|
|
|
{
|
|
|
|
|
StaticMesh->Modify();
|
|
|
|
|
UE::MeshDescription::FStaticMeshBuildSettingChange SettingsChange;
|
|
|
|
|
SettingsChange.AutoGeneratedNormals = UE::MeshDescription::EBuildSettingBoolChange::Disable;
|
|
|
|
|
UE::MeshDescription::ConfigureBuildSettings(StaticMesh, 0, SettingsChange);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-06 12:42:19 -05:00
|
|
|
const FDynamicMesh3* NewDynamicMesh = Results[ComponentIdx].Mesh.Get();
|
2021-12-09 18:21:09 -05:00
|
|
|
if (NewDynamicMesh)
|
|
|
|
|
{
|
2022-04-19 18:10:11 -04:00
|
|
|
if (bool bTopologyChanged = BasicProperties->WillTopologyChange())
|
|
|
|
|
{
|
|
|
|
|
// Tool may have changed the topology of the normal overlay (according to the specified tool properties), so we can't simply update the target mesh.
|
|
|
|
|
// Passing in bTopologyChanged = true will trigger the slower Convert function rather than the fast Update function.
|
|
|
|
|
UE::ToolTarget::CommitMeshDescriptionUpdateViaDynamicMesh(Targets[ComponentIdx], *NewDynamicMesh, bTopologyChanged);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// The tool didn't change the overlay topology so there's a chance we can do a fast path Update of the normal attributes.
|
|
|
|
|
// This function will still check if there is a mismatch between the dynamic mesh and target mesh in terms of triangles/vertices, and
|
|
|
|
|
// if so it will do the full conversion.
|
|
|
|
|
constexpr bool bUpdateTangents = false;
|
|
|
|
|
UE::ToolTarget::CommitDynamicMeshNormalsUpdate(Targets[ComponentIdx], NewDynamicMesh, bUpdateTangents);
|
|
|
|
|
}
|
2021-12-09 18:21:09 -05:00
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GetToolManager()->EndUndoTransaction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|