You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- 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]
251 lines
7.4 KiB
C++
251 lines
7.4 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SplitMeshesTool.h"
|
|
#include "ComponentSourceInterfaces.h"
|
|
#include "InteractiveToolManager.h"
|
|
#include "ToolTargetManager.h"
|
|
#include "ToolBuilderUtil.h"
|
|
#include "ToolSetupUtil.h"
|
|
#include "ModelingToolTargetUtil.h"
|
|
#include "ModelingObjectsCreationAPI.h"
|
|
#include "Selection/ToolSelectionUtil.h"
|
|
#include "Selections/MeshConnectedComponents.h"
|
|
#include "DynamicMesh/MeshTransforms.h"
|
|
#include "DynamicSubmesh3.h"
|
|
|
|
#include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types)
|
|
using namespace UE::Geometry;
|
|
|
|
#define LOCTEXT_NAMESPACE "USplitMeshesTool"
|
|
|
|
/*
|
|
* ToolBuilder
|
|
*/
|
|
const FToolTargetTypeRequirements& USplitMeshesToolBuilder::GetTargetRequirements() const
|
|
{
|
|
static FToolTargetTypeRequirements TypeRequirements({
|
|
UMaterialProvider::StaticClass(),
|
|
UMeshDescriptionProvider::StaticClass(),
|
|
UPrimitiveComponentBackedTarget::StaticClass()
|
|
});
|
|
return TypeRequirements;
|
|
}
|
|
|
|
bool USplitMeshesToolBuilder::CanBuildTool(const FToolBuilderState& SceneState) const
|
|
{
|
|
return SceneState.TargetManager->CountSelectedAndTargetable(SceneState, GetTargetRequirements()) > 0;
|
|
}
|
|
|
|
UInteractiveTool* USplitMeshesToolBuilder::BuildTool(const FToolBuilderState& SceneState) const
|
|
{
|
|
USplitMeshesTool* NewTool = NewObject<USplitMeshesTool>(SceneState.ToolManager);
|
|
TArray<TObjectPtr<UToolTarget>> Targets = SceneState.TargetManager->BuildAllSelectedTargetable(SceneState, GetTargetRequirements());
|
|
NewTool->SetTargets(MoveTemp(Targets));
|
|
NewTool->SetWorld(SceneState.World);
|
|
return NewTool;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Tool
|
|
*/
|
|
|
|
|
|
void USplitMeshesTool::SetWorld(UWorld* World)
|
|
{
|
|
this->TargetWorld = World;
|
|
}
|
|
|
|
void USplitMeshesTool::Setup()
|
|
{
|
|
UInteractiveTool::Setup();
|
|
|
|
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);
|
|
|
|
BasicProperties = NewObject<USplitMeshesToolProperties>(this);
|
|
BasicProperties->RestoreProperties(this);
|
|
AddToolPropertySource(BasicProperties);
|
|
|
|
SourceMeshes.SetNum(Targets.Num());
|
|
for (int32 k = 0; k < Targets.Num(); ++k)
|
|
{
|
|
SourceMeshes[k].Mesh = UE::ToolTarget::GetDynamicMeshCopy(Targets[k], true);
|
|
SourceMeshes[k].Materials = UE::ToolTarget::GetMaterialSet(Targets[k]).Materials;
|
|
}
|
|
|
|
UpdateSplitMeshes();
|
|
|
|
SetToolDisplayName(LOCTEXT("ToolName", "Split"));
|
|
GetToolManager()->DisplayMessage(
|
|
LOCTEXT("OnStartTool", "Split Meshes into parts"),
|
|
EToolMessageLevel::UserNotification);
|
|
}
|
|
|
|
bool USplitMeshesTool::CanAccept() const
|
|
{
|
|
return Super::CanAccept();
|
|
}
|
|
|
|
void USplitMeshesTool::Shutdown(EToolShutdownType ShutdownType)
|
|
{
|
|
OutputTypeProperties->SaveProperties(this, TEXT("OutputTypeFromInputTool"));
|
|
BasicProperties->SaveProperties(this);
|
|
|
|
if (ShutdownType == EToolShutdownType::Accept)
|
|
{
|
|
GetToolManager()->BeginUndoTransaction(LOCTEXT("SplitMeshesToolTransactionName", "Split Meshes"));
|
|
|
|
TArray<AActor*> NewSelectedActors;
|
|
TSet<AActor*> DeleteActors;
|
|
|
|
for (int32 ti = 0; ti < Targets.Num(); ++ti)
|
|
{
|
|
FComponentsInfo& SplitInfo = SplitMeshes[ti];
|
|
if (SplitInfo.bNoComponents)
|
|
{
|
|
continue;
|
|
}
|
|
AActor* TargetActor = UE::ToolTarget::GetTargetActor(Targets[ti]);
|
|
check(TargetActor != nullptr);
|
|
DeleteActors.Add(TargetActor);
|
|
|
|
FTransform3d SourceTransform = UE::ToolTarget::GetLocalToWorldTransform(Targets[ti]);
|
|
FDynamicMesh3 SourceMesh = UE::ToolTarget::GetDynamicMeshCopy(Targets[ti], true);
|
|
FString AssetName = TargetActor->GetName();
|
|
|
|
FCreateMeshObjectParams BaseMeshObjectParams;
|
|
BaseMeshObjectParams.TargetWorld = TargetWorld;
|
|
|
|
if (OutputTypeProperties->OutputType == UCreateMeshObjectTypeProperties::AutoIdentifier)
|
|
{
|
|
UE::ToolTarget::ConfigureCreateMeshObjectParams(Targets[0], BaseMeshObjectParams);
|
|
}
|
|
else
|
|
{
|
|
OutputTypeProperties->ConfigureCreateMeshObjectParams(BaseMeshObjectParams);
|
|
}
|
|
|
|
int32 NumComponents = SplitInfo.Meshes.Num();
|
|
for (int32 k = 0; k < NumComponents; ++k)
|
|
{
|
|
FCreateMeshObjectParams NewMeshObjectParams = BaseMeshObjectParams;
|
|
NewMeshObjectParams.BaseName = FString::Printf(TEXT("%s_%d"), *AssetName, k);
|
|
FTransform3d PartTransform = SourceTransform;
|
|
PartTransform.SetTranslation(SourceTransform.GetTranslation() + SplitInfo.Origins[k]);
|
|
NewMeshObjectParams.Transform = (FTransform)PartTransform;
|
|
if (BasicProperties->bTransferMaterials)
|
|
{
|
|
NewMeshObjectParams.Materials = SplitInfo.Materials[k];
|
|
}
|
|
NewMeshObjectParams.SetMesh(MoveTemp(SplitInfo.Meshes[k]));
|
|
|
|
FCreateMeshObjectResult Result = UE::Modeling::CreateMeshObject(GetToolManager(), MoveTemp(NewMeshObjectParams));
|
|
if (Result.IsOK())
|
|
{
|
|
NewSelectedActors.Add(Result.NewActor);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (AActor* DeleteActor : DeleteActors)
|
|
{
|
|
DeleteActor->Destroy();
|
|
}
|
|
|
|
ToolSelectionUtil::SetNewActorSelection(GetToolManager(), NewSelectedActors);
|
|
|
|
GetToolManager()->EndUndoTransaction();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void USplitMeshesTool::UpdateSplitMeshes()
|
|
{
|
|
SplitMeshes.Reset();
|
|
SplitMeshes.SetNum(SourceMeshes.Num());
|
|
NoSplitCount = 0;
|
|
|
|
for (int32 si = 0; si < SourceMeshes.Num(); ++si)
|
|
{
|
|
FComponentsInfo& SplitInfo = SplitMeshes[si];
|
|
const FDynamicMesh3* SourceMesh = &SourceMeshes[si].Mesh;
|
|
const TArray<UMaterialInterface*> SourceMaterials = SourceMeshes[si].Materials;
|
|
|
|
FMeshConnectedComponents MeshComponents(SourceMesh);
|
|
MeshComponents.FindConnectedTriangles();
|
|
int32 NumComponents = MeshComponents.Num();
|
|
|
|
if (NumComponents < 2)
|
|
{
|
|
SplitInfo.bNoComponents = true;
|
|
NoSplitCount++;
|
|
continue;
|
|
}
|
|
SplitInfo.bNoComponents = false;
|
|
|
|
SplitInfo.Meshes.SetNum(NumComponents);
|
|
SplitInfo.Materials.SetNum(NumComponents);
|
|
SplitInfo.Origins.SetNum(NumComponents);
|
|
for (int32 k = 0; k < NumComponents; ++k)
|
|
{
|
|
FDynamicSubmesh3 SubmeshCalc(SourceMesh, MeshComponents[k].Indices);
|
|
FDynamicMesh3& Submesh = SubmeshCalc.GetSubmesh();
|
|
TArray<UMaterialInterface*> NewMaterials;
|
|
|
|
// remap materials
|
|
FDynamicMeshMaterialAttribute* MaterialIDs = Submesh.HasAttributes() ? Submesh.Attributes()->GetMaterialID() : nullptr;
|
|
if (MaterialIDs)
|
|
{
|
|
TArray<int32> UniqueIDs;
|
|
for (int32 tid : Submesh.TriangleIndicesItr())
|
|
{
|
|
int32 MaterialID = MaterialIDs->GetValue(tid);
|
|
int32 Index = UniqueIDs.IndexOfByKey(MaterialID);
|
|
if (Index == INDEX_NONE)
|
|
{
|
|
int32 NewMaterialID = UniqueIDs.Num();
|
|
UniqueIDs.Add(MaterialID);
|
|
NewMaterials.Add(SourceMaterials[MaterialID]);
|
|
MaterialIDs->SetValue(tid, NewMaterialID);
|
|
}
|
|
else
|
|
{
|
|
MaterialIDs->SetValue(tid, Index);
|
|
}
|
|
}
|
|
}
|
|
|
|
// reposition mesh
|
|
FAxisAlignedBox3d Bounds = Submesh.GetBounds();
|
|
MeshTransforms::Translate(Submesh, -Bounds.Center());
|
|
|
|
SplitInfo.Meshes[k] = MoveTemp(Submesh);
|
|
SplitInfo.Materials[k] = MoveTemp(NewMaterials);
|
|
SplitInfo.Origins[k] = Bounds.Center();
|
|
}
|
|
}
|
|
|
|
if (NoSplitCount > 0)
|
|
{
|
|
GetToolManager()->DisplayMessage(
|
|
FText::Format(LOCTEXT("NoComponentsMessage", "{0} of {1} Input Meshes cannot be Split."), NoSplitCount, SourceMeshes.Num()), EToolMessageLevel::UserWarning);
|
|
}
|
|
else
|
|
{
|
|
GetToolManager()->DisplayMessage(FText(), EToolMessageLevel::UserWarning);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|