You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb none #rnx #jira none #preflight 60c52c5db9446100014da02d [CL 16653115 by Ryan Schmidt in ue5-main branch]
320 lines
9.4 KiB
C++
320 lines
9.4 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "EditNormalsTool.h"
|
|
#include "InteractiveToolManager.h"
|
|
#include "ToolBuilderUtil.h"
|
|
|
|
#include "ToolSetupUtil.h"
|
|
|
|
#include "DynamicMesh/DynamicMesh3.h"
|
|
#include "BaseBehaviors/MultiClickSequenceInputBehavior.h"
|
|
#include "Selection/SelectClickedAction.h"
|
|
|
|
#include "MeshDescriptionToDynamicMesh.h"
|
|
#include "DynamicMeshToMeshDescription.h"
|
|
|
|
#include "InteractiveGizmoManager.h"
|
|
|
|
#include "AssetUtils/MeshDescriptionUtil.h"
|
|
#include "Engine/Classes/Engine/StaticMesh.h"
|
|
#include "Engine/Classes/Components/StaticMeshComponent.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 "UEditNormalsTool"
|
|
|
|
/*
|
|
* ToolBuilder
|
|
*/
|
|
|
|
|
|
const FToolTargetTypeRequirements& UEditNormalsToolBuilder::GetTargetRequirements() const
|
|
{
|
|
static FToolTargetTypeRequirements TypeRequirements({
|
|
UMeshDescriptionCommitter::StaticClass(),
|
|
UMeshDescriptionProvider::StaticClass(),
|
|
UPrimitiveComponentBackedTarget::StaticClass()
|
|
});
|
|
return TypeRequirements;
|
|
}
|
|
|
|
bool UEditNormalsToolBuilder::CanBuildTool(const FToolBuilderState& SceneState) const
|
|
{
|
|
return SceneState.TargetManager->CountSelectedAndTargetable(SceneState, GetTargetRequirements()) > 0;
|
|
}
|
|
|
|
UInteractiveTool* UEditNormalsToolBuilder::BuildTool(const FToolBuilderState& SceneState) const
|
|
{
|
|
UEditNormalsTool* NewTool = NewObject<UEditNormalsTool>(SceneState.ToolManager);
|
|
|
|
TArray<TObjectPtr<UToolTarget>> Targets = SceneState.TargetManager->BuildAllSelectedTargetable(SceneState, GetTargetRequirements());
|
|
NewTool->SetTargets(MoveTemp(Targets));
|
|
NewTool->SetWorld(SceneState.World);
|
|
|
|
return NewTool;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Tool
|
|
*/
|
|
|
|
UEditNormalsToolProperties::UEditNormalsToolProperties()
|
|
{
|
|
bFixInconsistentNormals = false;
|
|
bInvertNormals = false;
|
|
bRecomputeNormals = true;
|
|
NormalCalculationMethod = ENormalCalculationMethod::AreaAngleWeighting;
|
|
SplitNormalMethod = ESplitNormalMethod::UseExistingTopology;
|
|
SharpEdgeAngleThreshold = 60;
|
|
bAllowSharpVertices = false;
|
|
}
|
|
|
|
UEditNormalsAdvancedProperties::UEditNormalsAdvancedProperties()
|
|
{
|
|
}
|
|
|
|
|
|
UEditNormalsTool::UEditNormalsTool()
|
|
{
|
|
}
|
|
|
|
void UEditNormalsTool::SetWorld(UWorld* World)
|
|
{
|
|
this->TargetWorld = World;
|
|
}
|
|
|
|
void UEditNormalsTool::Setup()
|
|
{
|
|
UInteractiveTool::Setup();
|
|
|
|
// hide input StaticMeshComponent
|
|
for (int32 ComponentIdx = 0, NumTargets = Targets.Num(); ComponentIdx < NumTargets; ComponentIdx++)
|
|
{
|
|
IPrimitiveComponentBackedTarget* TargetComponent = TargetComponentInterface(ComponentIdx);
|
|
TargetComponent->SetOwnerVisibility(false);
|
|
}
|
|
|
|
BasicProperties = NewObject<UEditNormalsToolProperties>(this, TEXT("Mesh Normals Settings"));
|
|
BasicProperties->RestoreProperties(this);
|
|
AddToolPropertySource(BasicProperties);
|
|
|
|
AdvancedProperties = NewObject<UEditNormalsAdvancedProperties>(this, TEXT("Advanced Settings"));
|
|
AdvancedProperties->RestoreProperties(this);
|
|
AddToolPropertySource(AdvancedProperties);
|
|
|
|
// initialize the PreviewMesh+BackgroundCompute object
|
|
UpdateNumPreviews();
|
|
|
|
|
|
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
{
|
|
Preview->InvalidateResult();
|
|
}
|
|
|
|
SetToolDisplayName(LOCTEXT("ToolName", "Edit Normals"));
|
|
GetToolManager()->DisplayMessage(
|
|
LOCTEXT("OnStartTool", "Configure or Recalculate Normals on a Mesh (disables autogenerated Normals)"),
|
|
EToolMessageLevel::UserNotification);
|
|
}
|
|
|
|
|
|
void UEditNormalsTool::UpdateNumPreviews()
|
|
{
|
|
int32 CurrentNumPreview = Previews.Num();
|
|
int32 TargetNumPreview = Targets.Num();
|
|
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;
|
|
OriginalDynamicMeshes[PreviewIdx] = MakeShared<FDynamicMesh3, ESPMode::ThreadSafe>();
|
|
FMeshDescriptionToDynamicMesh Converter;
|
|
Converter.Convert(TargetMeshProviderInterface(PreviewIdx)->GetMeshDescription(), *OriginalDynamicMeshes[PreviewIdx]);
|
|
|
|
UMeshOpPreviewWithBackgroundCompute* Preview = Previews.Add_GetRef(NewObject<UMeshOpPreviewWithBackgroundCompute>(OpFactory, "Preview"));
|
|
Preview->Setup(this->TargetWorld, OpFactory);
|
|
Preview->PreviewMesh->SetTangentsMode(EDynamicMeshComponentTangentsMode::AutoCalculated);
|
|
|
|
FComponentMaterialSet MaterialSet;
|
|
TargetMaterialInterface(PreviewIdx)->GetMaterialSet(MaterialSet);
|
|
Preview->ConfigureMaterials(MaterialSet.Materials,
|
|
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager())
|
|
);
|
|
|
|
Preview->SetVisibility(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UEditNormalsTool::Shutdown(EToolShutdownType ShutdownType)
|
|
{
|
|
BasicProperties->SaveProperties(this);
|
|
AdvancedProperties->SaveProperties(this);
|
|
|
|
// Restore (unhide) the source meshes
|
|
for (int32 ComponentIdx = 0, NumTargets = Targets.Num(); ComponentIdx < NumTargets; ComponentIdx++)
|
|
{
|
|
IPrimitiveComponentBackedTarget* TargetComponent = TargetComponentInterface(ComponentIdx);
|
|
TargetComponent->SetOwnerVisibility(true);
|
|
}
|
|
|
|
TArray<FDynamicMeshOpResult> Results;
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
{
|
|
Results.Add(Preview->Shutdown());
|
|
}
|
|
if (ShutdownType == EToolShutdownType::Accept)
|
|
{
|
|
GenerateAsset(Results);
|
|
}
|
|
}
|
|
|
|
TUniquePtr<FDynamicMeshOperator> UEditNormalsOperatorFactory::MakeNewOperator()
|
|
{
|
|
TUniquePtr<FEditNormalsOp> NormalsOp = MakeUnique<FEditNormalsOp>();
|
|
NormalsOp->bFixInconsistentNormals = Tool->BasicProperties->bFixInconsistentNormals;
|
|
NormalsOp->bInvertNormals = Tool->BasicProperties->bInvertNormals;
|
|
NormalsOp->bRecomputeNormals = Tool->BasicProperties->bRecomputeNormals;
|
|
NormalsOp->SplitNormalMethod = Tool->BasicProperties->SplitNormalMethod;
|
|
NormalsOp->bAllowSharpVertices = Tool->BasicProperties->bAllowSharpVertices;
|
|
NormalsOp->NormalCalculationMethod = Tool->BasicProperties->NormalCalculationMethod;
|
|
NormalsOp->NormalSplitThreshold = Tool->BasicProperties->SharpEdgeAngleThreshold;
|
|
|
|
FTransform LocalToWorld = Tool->TargetComponentInterface(ComponentIndex)->GetWorldTransform();
|
|
NormalsOp->OriginalMesh = Tool->OriginalDynamicMeshes[ComponentIndex];
|
|
|
|
NormalsOp->SetTransform(LocalToWorld);
|
|
|
|
return NormalsOp;
|
|
}
|
|
|
|
|
|
|
|
void UEditNormalsTool::Render(IToolsContextRenderAPI* RenderAPI)
|
|
{
|
|
}
|
|
|
|
void UEditNormalsTool::OnTick(float DeltaTime)
|
|
{
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
{
|
|
Preview->Tick(DeltaTime);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if WITH_EDITOR
|
|
void UEditNormalsTool::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
UpdateNumPreviews();
|
|
for (UMeshOpPreviewWithBackgroundCompute* Preview : Previews)
|
|
{
|
|
Preview->InvalidateResult();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void UEditNormalsTool::OnPropertyModified(UObject* PropertySet, FProperty* Property)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
return Super::CanAccept();
|
|
}
|
|
|
|
|
|
void UEditNormalsTool::GenerateAsset(const TArray<FDynamicMeshOpResult>& Results)
|
|
{
|
|
GetToolManager()->BeginUndoTransaction(LOCTEXT("EditNormalsToolTransactionName", "Edit Normals Tool"));
|
|
|
|
check(Results.Num() == Targets.Num());
|
|
|
|
for (int32 ComponentIdx = 0; ComponentIdx < Targets.Num(); ComponentIdx++)
|
|
{
|
|
// disable auto-generated normals StaticMesh build setting
|
|
UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(TargetComponentInterface(ComponentIdx)->GetOwnerComponent());
|
|
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);
|
|
}
|
|
}
|
|
|
|
check(Results[ComponentIdx].Mesh.Get() != nullptr);
|
|
TargetMeshCommitterInterface(ComponentIdx)->CommitMeshDescription([&Results, &ComponentIdx, this](const IMeshDescriptionCommitter::FCommitterParams& CommitParams)
|
|
{
|
|
FDynamicMeshToMeshDescription Converter;
|
|
|
|
if (BasicProperties->WillTopologyChange() || !FDynamicMeshToMeshDescription::HaveMatchingElementCounts(Results[ComponentIdx].Mesh.Get(), CommitParams.MeshDescriptionOut, false, true))
|
|
{
|
|
// full conversion if normal topology changed or faces were inverted
|
|
Converter.Convert(Results[ComponentIdx].Mesh.Get(), *CommitParams.MeshDescriptionOut);
|
|
}
|
|
else
|
|
{
|
|
// otherwise just copy attributes
|
|
const bool bUpdateNormals = true;
|
|
const bool bCopyOverlayTangents = false;
|
|
const bool bCopyOverlayUVs = false;
|
|
Converter.UpdateAttributes(Results[ComponentIdx].Mesh.Get(), *CommitParams.MeshDescriptionOut, bUpdateNormals, bCopyOverlayTangents, bCopyOverlayUVs);
|
|
}
|
|
});
|
|
}
|
|
|
|
GetToolManager()->EndUndoTransaction();
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|