// 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> 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(this); TransformProperties->RestoreProperties(this); AddToolPropertySource(TransformProperties); OutputTypeProperties = NewObject(this); OutputTypeProperties->InitializeDefaultWithAuto(); OutputTypeProperties->OutputType = UCreateMeshObjectTypeProperties::AutoIdentifier; OutputTypeProperties->RestoreProperties(this, TEXT("OutputTypeFromInputTool")); OutputTypeProperties->WatchProperty(OutputTypeProperties->OutputType, [this](FString) { OutputTypeProperties->UpdatePropertyVisibility(); }); AddToolPropertySource(OutputTypeProperties); HandleSourcesProperties = NewObject(this); HandleSourcesProperties->RestoreProperties(this); AddToolPropertySource(HandleSourcesProperties); Preview = NewObject(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(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 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(UpdateTarget); IMeshDescriptionCommitter* TargetMeshCommitter = Cast(UpdateTarget); IMaterialProvider* TargetMaterial = Cast(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 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