Files
UnrealEngineUWP/Engine/Plugins/Experimental/MeshModelingToolset/Source/ModelingComponents/Private/BaseTools/BaseCreateFromSelectedTool.cpp
Ryan Schmidt b02f4829db ModelingTools: DynamicMeshActor support Tools and Settings.
- Move UCreateMeshObjectTypeProperties to ModelingComponents and update all include sites
- Added Editor Modeling Mode settings to Enable/Disable showing DynamicMeshActor creation options, and select default mesh object type. Removed CVarEnableDynamicMeshActors.
- Added optional 'Auto' output mesh type to UCreateMeshObjectTypeProperties. This can be used in Tools that have input objects and want to allow optional conversion, but default to 'just use input mesh object type'.
- Added ConvertMeshesTool, this does in-place conversion between Mesh Object types for a set of selected objects (Duplicate tool can also do this, but only for a single object, and functionality is expected to further diverge)
- Added SplitMeshesTool, decomposes a mesh into parts and creates a new output object for each part
- CombineMeshesTool now supports variable output object type. Cleaned up internals.

#rb none
#rnx
#jira none
#preflight 60d3bc76b4bb42000195eccf

[CL 16768010 by Ryan Schmidt in ue5-main branch]
2021-06-23 22:13:32 -04:00

395 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BaseTools/BaseCreateFromSelectedTool.h"
#include "InteractiveToolManager.h"
#include "ToolBuilderUtil.h"
#include "ToolSetupUtil.h"
#include "Components/DynamicMeshComponent.h"
#include "BaseGizmos/TransformGizmoUtil.h"
#include "Async/Async.h"
#include "DynamicMesh/MeshNormals.h"
#include "DynamicMesh/MeshTransforms.h"
#include "DynamicMeshToMeshDescription.h"
#include "MeshDescriptionToDynamicMesh.h"
#include "ModelingObjectsCreationAPI.h"
#include "Selection/ToolSelectionUtil.h"
#include "TargetInterfaces/MeshDescriptionCommitter.h"
#include "TargetInterfaces/MeshDescriptionProvider.h"
#include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
#include "TargetInterfaces/MaterialProvider.h"
#include "TargetInterfaces/AssetBackedTarget.h"
#include "ToolTargetManager.h"
#include "ModelingToolTargetUtil.h"
#define LOCTEXT_NAMESPACE "UBaseCreateFromSelectedTool"
using namespace UE::Geometry;
/*
* ToolBuilder
*/
const FToolTargetTypeRequirements& UBaseCreateFromSelectedToolBuilder::GetTargetRequirements() const
{
static FToolTargetTypeRequirements TypeRequirements({
UMeshDescriptionCommitter::StaticClass(),
UMeshDescriptionProvider::StaticClass(),
UPrimitiveComponentBackedTarget::StaticClass(),
UMaterialProvider::StaticClass()
});
return TypeRequirements;
}
bool UBaseCreateFromSelectedToolBuilder::CanBuildTool(const FToolBuilderState& SceneState) const
{
int32 ComponentCount = SceneState.TargetManager->CountSelectedAndTargetable(SceneState, GetTargetRequirements());
return ComponentCount >= MinComponentsSupported() && (!MaxComponentsSupported().IsSet() || ComponentCount <= MaxComponentsSupported().GetValue());
}
UInteractiveTool* UBaseCreateFromSelectedToolBuilder::BuildTool(const FToolBuilderState& SceneState) const
{
UBaseCreateFromSelectedTool* NewTool = MakeNewToolInstance(SceneState.ToolManager);
TArray<TObjectPtr<UToolTarget>> Targets = SceneState.TargetManager->BuildAllSelectedTargetable(SceneState, GetTargetRequirements());
NewTool->SetTargets(MoveTemp(Targets));
NewTool->SetWorld(SceneState.World);
return NewTool;
}
/*
* Tool
*/
void UBaseCreateFromSelectedTool::Setup()
{
UInteractiveTool::Setup();
// hide input StaticMeshComponents
for (int32 ComponentIdx = 0; ComponentIdx < Targets.Num(); ComponentIdx++)
{
TargetComponentInterface(ComponentIdx)->SetOwnerVisibility(false);
}
// initialize our properties
SetupProperties();
TransformProperties = NewObject<UTransformInputsToolProperties>(this);
TransformProperties->RestoreProperties(this);
AddToolPropertySource(TransformProperties);
OutputTypeProperties = NewObject<UCreateMeshObjectTypeProperties>(this);
OutputTypeProperties->InitializeDefaultWithAuto();
OutputTypeProperties->OutputType = UCreateMeshObjectTypeProperties::AutoIdentifier;
OutputTypeProperties->RestoreProperties(this, TEXT("OutputTypeFromInputTool"));
OutputTypeProperties->WatchProperty(OutputTypeProperties->OutputType, [this](FString) { OutputTypeProperties->UpdatePropertyVisibility(); });
AddToolPropertySource(OutputTypeProperties);
HandleSourcesProperties = NewObject<UBaseCreateFromSelectedHandleSourceProperties>(this);
HandleSourcesProperties->RestoreProperties(this);
AddToolPropertySource(HandleSourcesProperties);
Preview = NewObject<UMeshOpPreviewWithBackgroundCompute>(this);
Preview->Setup(this->TargetWorld, this);
SetPreviewCallbacks();
Preview->OnMeshUpdated.AddLambda(
[this](const UMeshOpPreviewWithBackgroundCompute* UpdatedPreview)
{
UpdateAcceptWarnings(UpdatedPreview->HaveEmptyResult() ? EAcceptWarning::EmptyForbidden : EAcceptWarning::NoWarning);
}
);
SetTransformGizmos();
ConvertInputsAndSetPreviewMaterials(true);
// output name fields
HandleSourcesProperties->OutputName = PrefixWithSourceNameIfSingleSelection(GetCreatedAssetName());
HandleSourcesProperties->WatchProperty(HandleSourcesProperties->WriteOutputTo, [&](EBaseCreateFromSelectedTargetType NewType)
{
SetToolPropertySourceEnabled(OutputTypeProperties, (NewType == EBaseCreateFromSelectedTargetType::NewAsset));
if (NewType == EBaseCreateFromSelectedTargetType::NewAsset)
{
HandleSourcesProperties->OutputAsset = TEXT("");
UpdateGizmoVisibility();
}
else
{
int32 Index = (HandleSourcesProperties->WriteOutputTo == EBaseCreateFromSelectedTargetType::FirstInputAsset) ? 0 : Targets.Num() - 1;
HandleSourcesProperties->OutputAsset = UE::Modeling::GetComponentAssetBaseName(TargetComponentInterface(Index)->GetOwnerComponent(), false);
// Reset the hidden gizmo to its initial position
FTransform ComponentTransform = TargetComponentInterface(Index)->GetWorldTransform();
TransformGizmos[Index]->SetNewGizmoTransform(ComponentTransform, true);
UpdateGizmoVisibility();
}
});
Preview->InvalidateResult();
}
int32 UBaseCreateFromSelectedTool::GetHiddenGizmoIndex() const
{
if (HandleSourcesProperties->WriteOutputTo == EBaseCreateFromSelectedTargetType::NewAsset)
{
return -1;
}
else
{
return (HandleSourcesProperties->WriteOutputTo == EBaseCreateFromSelectedTargetType::FirstInputAsset) ? 0 : Targets.Num() - 1;
}
}
void UBaseCreateFromSelectedTool::SetWorld(UWorld* World)
{
this->TargetWorld = World;
}
void UBaseCreateFromSelectedTool::OnTick(float DeltaTime)
{
for (UTransformGizmo* Gizmo : TransformGizmos)
{
Gizmo->bSnapToWorldGrid = TransformProperties->bSnapToWorldGrid;
}
Preview->Tick(DeltaTime);
}
void UBaseCreateFromSelectedTool::UpdateGizmoVisibility()
{
for (int32 GizmoIndex = 0; GizmoIndex < TransformGizmos.Num(); GizmoIndex++)
{
UTransformGizmo* Gizmo = TransformGizmos[GizmoIndex];
Gizmo->SetVisibility(TransformProperties->bShowTransformUI && GizmoIndex != GetHiddenGizmoIndex());
}
}
void UBaseCreateFromSelectedTool::SetTransformGizmos()
{
UInteractiveGizmoManager* GizmoManager = GetToolManager()->GetPairedGizmoManager();
for (int ComponentIdx = 0; ComponentIdx < Targets.Num(); ComponentIdx++)
{
UTransformProxy* Proxy = TransformProxies.Add_GetRef(NewObject<UTransformProxy>(this));
UTransformGizmo* Gizmo = TransformGizmos.Add_GetRef(UE::TransformGizmoUtil::Create3AxisTransformGizmo(GizmoManager, this));
Proxy->SetTransform(TargetComponentInterface(ComponentIdx)->GetWorldTransform());
Gizmo->SetActiveTarget(Proxy, GetToolManager());
Proxy->OnTransformChanged.AddUObject(this, &UBaseCreateFromSelectedTool::TransformChanged);
}
UpdateGizmoVisibility();
}
void UBaseCreateFromSelectedTool::TransformChanged(UTransformProxy* Proxy, FTransform Transform)
{
Preview->InvalidateResult();
}
FText UBaseCreateFromSelectedTool::GetActionName() const
{
return LOCTEXT("BaseCreateFromSelectedTool", "Generated Mesh");
}
TArray<UMaterialInterface*> UBaseCreateFromSelectedTool::GetOutputMaterials() const
{
return Preview->StandardMaterials;
}
void UBaseCreateFromSelectedTool::GenerateAsset(const FDynamicMeshOpResult& OpResult)
{
if (OpResult.Mesh.Get() == nullptr) return;
FTransform3d NewTransform;
if (Targets.Num() == 1) // in the single-selection case, shove the result back into the original component space
{
FTransform3d ToSourceComponentSpace = (FTransform3d)TargetComponentInterface(0)->GetWorldTransform().Inverse();
MeshTransforms::ApplyTransform(*OpResult.Mesh, ToSourceComponentSpace);
NewTransform = (FTransform3d)TargetComponentInterface(0)->GetWorldTransform();
}
else // in the multi-selection case, center the pivot for the combined result
{
FVector3d Center = OpResult.Mesh->GetCachedBounds().Center();
double Rescale = OpResult.Transform.GetScale().X;
FTransform3d LocalTransform(-Center * Rescale);
LocalTransform.SetScale(FVector3d(Rescale, Rescale, Rescale));
MeshTransforms::ApplyTransform(*OpResult.Mesh, LocalTransform);
NewTransform = OpResult.Transform;
NewTransform.SetScale(FVector3d::One());
NewTransform.SetTranslation(NewTransform.GetTranslation() + NewTransform.TransformVector(Center * Rescale));
}
// max len explicitly enforced here, would ideally notify user
FString UseBaseName = HandleSourcesProperties->OutputName.Left(250);
if (UseBaseName.IsEmpty())
{
UseBaseName = PrefixWithSourceNameIfSingleSelection(GetCreatedAssetName());
}
FCreateMeshObjectParams NewMeshObjectParams;
NewMeshObjectParams.TargetWorld = TargetWorld;
NewMeshObjectParams.Transform = (FTransform)NewTransform;
NewMeshObjectParams.BaseName = UseBaseName;
NewMeshObjectParams.Materials = GetOutputMaterials();
NewMeshObjectParams.SetMesh(OpResult.Mesh.Get());
if (OutputTypeProperties->OutputType == UCreateMeshObjectTypeProperties::AutoIdentifier)
{
UE::ToolTarget::ConfigureCreateMeshObjectParams(Targets[0], NewMeshObjectParams);
}
else
{
OutputTypeProperties->ConfigureCreateMeshObjectParams(NewMeshObjectParams);
}
FCreateMeshObjectResult Result = UE::Modeling::CreateMeshObject(GetToolManager(), MoveTemp(NewMeshObjectParams));
if (Result.IsOK() && Result.NewActor != nullptr)
{
ToolSelectionUtil::SetNewActorSelection(GetToolManager(), Result.NewActor);
}
}
void UBaseCreateFromSelectedTool::UpdateAsset(const FDynamicMeshOpResult& Result, UToolTarget* UpdateTarget)
{
check(Result.Mesh.Get() != nullptr);
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(UpdateTarget);
IMeshDescriptionCommitter* TargetMeshCommitter = Cast<IMeshDescriptionCommitter>(UpdateTarget);
IMaterialProvider* TargetMaterial = Cast<IMaterialProvider>(UpdateTarget);
FTransform3d TargetToWorld = (FTransform3d)TargetComponent->GetWorldTransform();
FTransform3d ResultTransform = Result.Transform;
MeshTransforms::ApplyTransform(*Result.Mesh, ResultTransform);
MeshTransforms::ApplyTransformInverse(*Result.Mesh, TargetToWorld);
TargetMeshCommitter->CommitMeshDescription([&](const IMeshDescriptionCommitter::FCommitterParams& CommitParams)
{
FDynamicMeshToMeshDescription Converter;
Converter.Convert(Result.Mesh.Get(), *CommitParams.MeshDescriptionOut);
});
FComponentMaterialSet MaterialSet;
MaterialSet.Materials = GetOutputMaterials();
TargetMaterial->CommitMaterialSetUpdate(MaterialSet, true);
}
FString UBaseCreateFromSelectedTool::PrefixWithSourceNameIfSingleSelection(const FString& AssetName) const
{
if (Targets.Num() == 1)
{
FString CurName = UE::Modeling::GetComponentAssetBaseName(TargetComponentInterface(0)->GetOwnerComponent());
return FString::Printf(TEXT("%s_%s"), *CurName, *AssetName);
}
else
{
return AssetName;
}
}
void UBaseCreateFromSelectedTool::OnPropertyModified(UObject* PropertySet, FProperty* Property)
{
if (Property && (Property->GetFName() == GET_MEMBER_NAME_CHECKED(UTransformInputsToolProperties, bShowTransformUI)))
{
UpdateGizmoVisibility();
}
else if (Property &&
( PropertySet == HandleSourcesProperties
|| Property->GetFName() == GET_MEMBER_NAME_CHECKED(UTransformInputsToolProperties, bSnapToWorldGrid)
))
{
// nothing
}
else
{
Preview->InvalidateResult();
}
}
void UBaseCreateFromSelectedTool::Shutdown(EToolShutdownType ShutdownType)
{
SaveProperties();
HandleSourcesProperties->SaveProperties(this);
OutputTypeProperties->SaveProperties(this, TEXT("OutputTypeFromInputTool"));
TransformProperties->SaveProperties(this);
FDynamicMeshOpResult Result = Preview->Shutdown();
// Restore (unhide) the source meshes
for (int32 ComponentIdx = 0; ComponentIdx < Targets.Num(); ComponentIdx++)
{
TargetComponentInterface(ComponentIdx)->SetOwnerVisibility(true);
}
if (ShutdownType == EToolShutdownType::Accept)
{
GetToolManager()->BeginUndoTransaction(GetActionName());
// Generate the result
AActor* KeepActor = nullptr;
if (HandleSourcesProperties->WriteOutputTo == EBaseCreateFromSelectedTargetType::NewAsset)
{
GenerateAsset(Result);
}
else
{
int32 TargetIndex = (HandleSourcesProperties->WriteOutputTo == EBaseCreateFromSelectedTargetType::FirstInputAsset) ? 0 : (Targets.Num() - 1);
KeepActor = TargetComponentInterface(TargetIndex)->GetOwnerActor();
UpdateAsset(Result, Targets[TargetIndex]);
}
TArray<AActor*> Actors;
for (int32 ComponentIdx = 0; ComponentIdx < Targets.Num(); ComponentIdx++)
{
AActor* Actor = TargetComponentInterface(ComponentIdx)->GetOwnerActor();
if (Actor != KeepActor)
{
Actors.Add(Actor);
}
}
HandleSourcesProperties->ApplyMethod(Actors, GetToolManager());
if (KeepActor != nullptr)
{
// select the actor we kept
ToolSelectionUtil::SetNewActorSelection(GetToolManager(), KeepActor);
}
GetToolManager()->EndUndoTransaction();
}
UInteractiveGizmoManager* GizmoManager = GetToolManager()->GetPairedGizmoManager();
GizmoManager->DestroyAllGizmosByOwner(this);
}
bool UBaseCreateFromSelectedTool::CanAccept() const
{
return Super::CanAccept() && Preview->HaveValidNonEmptyResult();
}
#undef LOCTEXT_NAMESPACE