2019-09-12 13:55:17 -04:00
|
|
|
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "MergeMeshesTool.h"
|
|
|
|
|
#include "InteractiveToolManager.h"
|
|
|
|
|
#include "ToolBuilderUtil.h"
|
|
|
|
|
#include "ToolSetupUtil.h"
|
|
|
|
|
#include "AssetGenerationUtil.h"
|
|
|
|
|
#include "Selection/ToolSelectionUtil.h"
|
|
|
|
|
|
|
|
|
|
#include "DynamicMesh3.h"
|
|
|
|
|
#include "MeshDescriptionToDynamicMesh.h"
|
|
|
|
|
|
|
|
|
|
#include "CompositionOps/VoxelMergeMeshesOp.h"
|
|
|
|
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "UMergeMeshesTool"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ToolBuilder
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool UMergeMeshesToolBuilder::CanBuildTool(const FToolBuilderState& SceneState) const
|
|
|
|
|
{
|
|
|
|
|
bool bHasBuildAPI = (this->AssetAPI != nullptr);
|
|
|
|
|
bool bHasComponents = ToolBuilderUtil::CountComponents(SceneState, CanMakeComponentTarget) > 1;
|
|
|
|
|
return ( bHasBuildAPI && bHasComponents );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UInteractiveTool* UMergeMeshesToolBuilder::BuildTool(const FToolBuilderState& SceneState) const
|
|
|
|
|
{
|
|
|
|
|
UMergeMeshesTool* NewTool = NewObject<UMergeMeshesTool>(SceneState.ToolManager);
|
|
|
|
|
|
|
|
|
|
TArray<UActorComponent*> Components = ToolBuilderUtil::FindAllComponents(SceneState, CanMakeComponentTarget);
|
|
|
|
|
check(Components.Num() > 0);
|
|
|
|
|
|
|
|
|
|
TArray<TUniquePtr<FPrimitiveComponentTarget>> ComponentTargets;
|
|
|
|
|
for (UActorComponent* ActorComponent : Components)
|
|
|
|
|
{
|
|
|
|
|
auto* MeshComponent = Cast<UPrimitiveComponent>(ActorComponent);
|
|
|
|
|
if ( MeshComponent )
|
|
|
|
|
{
|
|
|
|
|
ComponentTargets.Add(MakeComponentTarget(MeshComponent));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NewTool->SetSelection(MoveTemp(ComponentTargets));
|
|
|
|
|
NewTool->SetWorld(SceneState.World);
|
|
|
|
|
NewTool->SetAssetAPI(AssetAPI);
|
|
|
|
|
|
|
|
|
|
return NewTool;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Tool
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
UMergeMeshesTool::UMergeMeshesTool()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMergeMeshesTool::SetWorld(UWorld* World)
|
|
|
|
|
{
|
|
|
|
|
this->TargetWorld = World;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMergeMeshesTool::SetAssetAPI(IToolsContextAssetAPI* AssetAPIIn)
|
|
|
|
|
{
|
|
|
|
|
this->AssetAPI = AssetAPIIn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UMergeMeshesTool::Setup()
|
|
|
|
|
{
|
|
|
|
|
UInteractiveTool::Setup();
|
|
|
|
|
|
|
|
|
|
MergeProps = NewObject<UMergeMeshesToolProperties>();
|
|
|
|
|
AddToolPropertySource(MergeProps);
|
|
|
|
|
|
2019-09-24 13:12:06 -04:00
|
|
|
MeshStatisticsProperties = NewObject<UMeshStatisticsProperties>(this);
|
|
|
|
|
AddToolPropertySource(MeshStatisticsProperties);
|
|
|
|
|
|
2019-09-12 13:55:17 -04:00
|
|
|
// Hide the source meshes
|
|
|
|
|
for (auto& ComponentTarget : ComponentTargets)
|
|
|
|
|
{
|
|
|
|
|
ComponentTarget->SetOwnerVisibility(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// save transformed version of input meshes (maybe this could happen in the Operator?)
|
|
|
|
|
CacheInputMeshes();
|
|
|
|
|
|
|
|
|
|
// initialize the PreviewMesh+BackgroundCompute object
|
|
|
|
|
Preview = NewObject<UMeshOpPreviewWithBackgroundCompute>(this, "Preview");
|
|
|
|
|
Preview->Setup(this->TargetWorld, this);
|
2019-09-24 13:12:06 -04:00
|
|
|
Preview->OnMeshUpdated.AddLambda([this](UMeshOpPreviewWithBackgroundCompute* Compute) {
|
|
|
|
|
MeshStatisticsProperties->Update(*Compute->PreviewMesh->GetPreviewDynamicMesh());
|
|
|
|
|
});
|
2019-09-23 16:41:56 -04:00
|
|
|
|
|
|
|
|
CreateLowQualityPreview(); // update the preview with a low-quality result
|
|
|
|
|
|
2019-09-12 13:55:17 -04:00
|
|
|
Preview->ConfigureMaterials(
|
|
|
|
|
// TODO: This is is very likely a bug waiting to happen
|
|
|
|
|
ToolSetupUtil::GetDefaultMaterial(GetToolManager(), ComponentTargets[0]->GetMaterial(0)),
|
|
|
|
|
ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager())
|
|
|
|
|
);
|
2019-09-23 16:41:56 -04:00
|
|
|
|
2019-09-12 13:55:17 -04:00
|
|
|
Preview->InvalidateResult(); // start compute
|
2019-09-24 13:12:06 -04:00
|
|
|
|
2019-09-12 13:55:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UMergeMeshesTool::Shutdown(EToolShutdownType ShutdownType)
|
|
|
|
|
{
|
2019-10-02 12:05:44 -04:00
|
|
|
FDynamicMeshOpResult Result = Preview->Shutdown();
|
2019-09-12 13:55:17 -04:00
|
|
|
if (ShutdownType == EToolShutdownType::Accept)
|
|
|
|
|
{
|
2019-09-20 16:16:09 -04:00
|
|
|
GetToolManager()->BeginUndoTransaction(LOCTEXT("MergeMeshes", "Merge Meshes"));
|
|
|
|
|
|
2019-09-12 13:55:17 -04:00
|
|
|
GenerateAsset(Result);
|
2019-09-20 16:16:09 -04:00
|
|
|
|
2019-09-20 15:16:56 -04:00
|
|
|
for (auto& ComponentTarget : ComponentTargets)
|
|
|
|
|
{
|
|
|
|
|
ComponentTarget->SetOwnerVisibility(true);
|
|
|
|
|
AActor* Actor = ComponentTarget->GetOwnerActor();
|
2019-09-24 13:12:06 -04:00
|
|
|
if (MergeProps->bDeleteInputActors)
|
2019-09-20 16:16:09 -04:00
|
|
|
{
|
|
|
|
|
Actor->Destroy();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// just hide the result.
|
|
|
|
|
Actor->SetIsTemporarilyHiddenInEditor(true);
|
|
|
|
|
}
|
2019-09-20 15:16:56 -04:00
|
|
|
}
|
2019-09-20 16:16:09 -04:00
|
|
|
|
|
|
|
|
GetToolManager()->EndUndoTransaction();
|
|
|
|
|
|
2019-09-20 15:16:56 -04:00
|
|
|
}
|
|
|
|
|
else
|
2019-09-12 13:55:17 -04:00
|
|
|
{
|
2019-09-20 15:16:56 -04:00
|
|
|
// Restore (unhide) the source meshes
|
|
|
|
|
for (auto& ComponentTarget : ComponentTargets)
|
|
|
|
|
{
|
|
|
|
|
ComponentTarget->SetOwnerVisibility(true);
|
|
|
|
|
}
|
2019-09-12 13:55:17 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UMergeMeshesTool::Tick(float DeltaTime)
|
|
|
|
|
{
|
|
|
|
|
Preview->Tick(DeltaTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UMergeMeshesTool::Render(IToolsContextRenderAPI* RenderAPI)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool UMergeMeshesTool::HasAccept() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UMergeMeshesTool::CanAccept() const
|
|
|
|
|
{
|
|
|
|
|
return Preview->HaveValidResult();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UMergeMeshesTool::OnPropertyModified(UObject* PropertySet, UProperty* Property)
|
|
|
|
|
{
|
|
|
|
|
Preview->InvalidateResult();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TSharedPtr<FDynamicMeshOperator> UMergeMeshesTool::MakeNewOperator()
|
|
|
|
|
{
|
|
|
|
|
TSharedPtr<FVoxelMergeMeshesOp> MergeOp = MakeShared<FVoxelMergeMeshesOp>();
|
2019-09-13 14:24:58 -04:00
|
|
|
MergeOp->VoxelCount = MergeProps->VoxelCount;
|
2019-09-24 13:12:06 -04:00
|
|
|
MergeOp->AdaptivityD = MergeProps->MeshAdaptivity;
|
|
|
|
|
MergeOp->IsoSurfaceD = MergeProps->OffsetDistance;
|
2019-09-13 14:24:58 -04:00
|
|
|
MergeOp->bAutoSimplify = MergeProps->bAutoSimplify;
|
2019-09-12 13:55:17 -04:00
|
|
|
MergeOp->InputMeshArray = InputMeshes;
|
|
|
|
|
return MergeOp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UMergeMeshesTool::CacheInputMeshes()
|
|
|
|
|
{
|
|
|
|
|
InputMeshes = MakeShared<TArray<IVoxelBasedCSG::FPlacedMesh>>();
|
|
|
|
|
|
|
|
|
|
// Package the selected meshes and transforms for consumption by the CSGTool
|
|
|
|
|
for (auto& ComponentTarget : ComponentTargets)
|
|
|
|
|
{
|
|
|
|
|
IVoxelBasedCSG::FPlacedMesh PlacedMesh;
|
|
|
|
|
PlacedMesh.Mesh = ComponentTarget->GetMesh();
|
|
|
|
|
PlacedMesh.Transform = ComponentTarget->GetWorldTransform();
|
|
|
|
|
InputMeshes->Add(PlacedMesh);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-23 16:41:56 -04:00
|
|
|
void UMergeMeshesTool::CreateLowQualityPreview()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
FProgressCancel NullInterrupter;
|
|
|
|
|
FVoxelMergeMeshesOp MergeMeshesOp;
|
|
|
|
|
|
|
|
|
|
MergeMeshesOp.VoxelCount = 12;
|
|
|
|
|
MergeMeshesOp.AdaptivityD = 0.001;
|
|
|
|
|
MergeMeshesOp.bAutoSimplify = true;
|
|
|
|
|
MergeMeshesOp.InputMeshArray = InputMeshes;
|
|
|
|
|
|
|
|
|
|
MergeMeshesOp.CalculateResult(&NullInterrupter);
|
|
|
|
|
TUniquePtr<FDynamicMesh3> FastPreviewMesh = MergeMeshesOp.ExtractResult();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Preview->PreviewMesh->SetTransform((FTransform)MergeMeshesOp.GetResultTransform());
|
|
|
|
|
Preview->PreviewMesh->UpdatePreview(FastPreviewMesh.Get()); // copies the mesh @todo we could just give ownership to the Preview!
|
|
|
|
|
Preview->SetVisibility(true);
|
|
|
|
|
}
|
2019-09-12 13:55:17 -04:00
|
|
|
|
|
|
|
|
|
2019-10-02 12:05:44 -04:00
|
|
|
void UMergeMeshesTool::GenerateAsset(const FDynamicMeshOpResult& Result)
|
2019-09-12 13:55:17 -04:00
|
|
|
{
|
2019-10-02 12:05:44 -04:00
|
|
|
check(Result.Mesh.Get() != nullptr);
|
2019-09-12 13:55:17 -04:00
|
|
|
|
2019-09-20 16:16:09 -04:00
|
|
|
|
2019-09-12 13:55:17 -04:00
|
|
|
|
|
|
|
|
AActor* NewActor = AssetGenerationUtil::GenerateStaticMeshActor(
|
|
|
|
|
AssetAPI, TargetWorld,
|
2019-10-02 12:05:44 -04:00
|
|
|
Result.Mesh.Get(), Result.Transform, TEXT("MergedMesh"),
|
2019-09-12 13:55:17 -04:00
|
|
|
AssetGenerationUtil::GetDefaultAutoGeneratedAssetPath());
|
|
|
|
|
|
|
|
|
|
// select newly-created object
|
|
|
|
|
ToolSelectionUtil::SetNewActorSelection(GetToolManager(), NewActor);
|
|
|
|
|
|
2019-09-20 16:16:09 -04:00
|
|
|
|
2019-09-12 13:55:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|