// Copyright Epic Games, Inc. All Rights Reserved. #include "EditMeshMaterialsTool.h" #include "InteractiveToolManager.h" #include "ToolBuilderUtil.h" #include "Drawing/MeshDebugDrawing.h" #include "DynamicMeshEditor.h" #include "DynamicMesh/DynamicMeshChangeTracker.h" #include "Changes/ToolCommandChangeSequence.h" #include "Changes/MeshChange.h" #include "Util/ColorConstants.h" #include "Selections/MeshConnectedComponents.h" #include "MeshRegionBoundaryLoops.h" #include "DynamicMesh/MeshIndexUtil.h" #include "ToolSetupUtil.h" #include "TargetInterfaces/MaterialProvider.h" #include "TargetInterfaces/MeshDescriptionCommitter.h" #include "TargetInterfaces/PrimitiveComponentBackedTarget.h" #include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types) using namespace UE::Geometry; #define LOCTEXT_NAMESPACE "UEditMeshMaterialsTool" void UEditMeshMaterialsEditActions::PostMaterialAction(EEditMeshMaterialsToolActions Action) { if (ParentTool.IsValid() && Cast(ParentTool)) { Cast(ParentTool)->RequestMaterialAction(Action); } } /* * ToolBuilder */ UMeshSurfacePointTool* UEditMeshMaterialsToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const { UEditMeshMaterialsTool* SelectionTool = NewObject(SceneState.ToolManager); SelectionTool->SetWorld(SceneState.World); return SelectionTool; } void UEditMeshMaterialsTool::Setup() { UMeshSelectionTool::Setup(); SetToolDisplayName(LOCTEXT("ToolName", "Edit Materials")); PreviewMesh->ClearOverrideRenderMaterial(); IMaterialProvider* TargetMaterial = Cast(Target); FComponentMaterialSet AssetMaterials; TargetMaterial->GetMaterialSet(AssetMaterials, true); MaterialProps->Materials = AssetMaterials.Materials; CurrentMaterials = MaterialProps->Materials; InitialMaterialKey = GetMaterialKey(); MaterialProps->WatchProperty( [this](){ return GetMaterialKey(); }, [this](FMaterialSetKey NewKey) { OnMaterialSetChanged(); }); FComponentMaterialSet ComponentMaterials; TargetMaterial->GetMaterialSet(ComponentMaterials, false); if (ComponentMaterials != AssetMaterials) { GetToolManager()->DisplayMessage( LOCTEXT("MaterialWarning", "The selected Component has a different Material set than the underlying Asset. Asset materials are shown."), EToolMessageLevel::UserWarning); } } UMeshSelectionToolActionPropertySet* UEditMeshMaterialsTool::CreateEditActions() { UEditMeshMaterialsEditActions* Actions = NewObject(this); Actions->Initialize(this); return Actions; } void UEditMeshMaterialsTool::AddSubclassPropertySets() { MaterialProps = NewObject(this); MaterialProps->RestoreProperties(this); AddToolPropertySource(MaterialProps); } void UEditMeshMaterialsTool::RequestMaterialAction(EEditMeshMaterialsToolActions ActionType) { if (bHavePendingAction) { return; } PendingSubAction = ActionType; bHavePendingSubAction = true; } void UEditMeshMaterialsTool::OnTick(float DeltaTime) { UMeshSelectionTool::OnTick(DeltaTime); if (bHavePendingSubAction) { ApplyMaterialAction(PendingSubAction); bHavePendingSubAction = false; PendingSubAction = EEditMeshMaterialsToolActions::NoAction; } } void UEditMeshMaterialsTool::ApplyMaterialAction(EEditMeshMaterialsToolActions ActionType) { switch (ActionType) { case EEditMeshMaterialsToolActions::AssignMaterial: AssignMaterialToSelectedTriangles(); break; } } void UEditMeshMaterialsTool::AssignMaterialToSelectedTriangles() { check(SelectionType == EMeshSelectionElementType::Face); TArray SelectedFaces = Selection->GetElements(EMeshSelectionElementType::Face); if (SelectedFaces.Num() == 0) { return; } TUniquePtr ChangeSeq = MakeUnique(); // clear current selection BeginChange(false); for (int tid : SelectedFaces) { ActiveSelectionChange->Add(tid); } Selection->RemoveIndices(EMeshSelectionElementType::Face, SelectedFaces); TUniquePtr SelectionChange = EndChange(); ChangeSeq->AppendChange(Selection, MoveTemp(SelectionChange)); int32 SetMaterialID = MaterialProps->SelectedMaterial; // assign new groups to triangles // note: using an FMeshChange is kind of overkill here TUniquePtr MeshChange = PreviewMesh->TrackedEditMesh( [&SelectedFaces, SetMaterialID](FDynamicMesh3& Mesh, FDynamicMeshChangeTracker& ChangeTracker) { if (Mesh.Attributes() && Mesh.Attributes()->HasMaterialID()) { FDynamicMeshMaterialAttribute* MaterialIDAttrib = Mesh.Attributes()->GetMaterialID(); for (int tid : SelectedFaces) { ChangeTracker.SaveTriangle(tid, true); MaterialIDAttrib->SetNewValue(tid, SetMaterialID); } } }); ChangeSeq->AppendChange(PreviewMesh, MoveTemp(MeshChange)); // emit combined change sequence GetToolManager()->EmitObjectChange(this, MoveTemp(ChangeSeq), LOCTEXT("MeshSelectionToolAssignMaterial", "Assign Material")); OnExternalSelectionChange(); bHaveModifiedMesh = true; } void UEditMeshMaterialsTool::OnMaterialSetChanged() { TUniquePtr MaterialChange = MakeUnique(); MaterialChange->MaterialsBefore = CurrentMaterials; MaterialChange->MaterialsAfter = MaterialProps->Materials; PreviewMesh->SetMaterials(MaterialProps->Materials); CurrentMaterials = MaterialProps->Materials; GetToolManager()->EmitObjectChange(this, MoveTemp(MaterialChange), LOCTEXT("MaterialSetChange", "Material Change")); bHaveModifiedMaterials = true; } void UEditMeshMaterialsTool::ExternalUpdateMaterialSet(const TArray& NewMaterialSet) { // Disable props so they don't update SetToolPropertySourceEnabled(MaterialProps, false); MaterialProps->Materials = NewMaterialSet; SetToolPropertySourceEnabled(MaterialProps, true); PreviewMesh->SetMaterials(MaterialProps->Materials); CurrentMaterials = MaterialProps->Materials; } void UEditMeshMaterialsTool::OnShutdown(EToolShutdownType ShutdownType) { // this is a bit of a hack, UMeshSelectionTool::OnShutdown will also do this... SelectionProps->SaveProperties(this); if (ShutdownType == EToolShutdownType::Accept) { GetToolManager()->BeginUndoTransaction(LOCTEXT("EditMeshMaterialsTransactionName", "Edit Materials")); if (GetMaterialKey() != InitialMaterialKey) { FComponentMaterialSet NewMaterialSet; NewMaterialSet.Materials = CurrentMaterials; Cast(Target)->CommitMaterialSetUpdate(NewMaterialSet, true); } UMeshSelectionTool::OnShutdown(ShutdownType); GetToolManager()->EndUndoTransaction(); } } bool UEditMeshMaterialsTool::FMaterialSetKey::operator!=(const FMaterialSetKey& Key2) const { int Num = Values.Num(); if (Key2.Values.Num() != Num) { return true; } for (int j = 0; j < Num; ++j) { if (Key2.Values[j] != Values[j]) { return true; } } return false; } UEditMeshMaterialsTool::FMaterialSetKey UEditMeshMaterialsTool::GetMaterialKey() { FMaterialSetKey Key; for (UMaterialInterface* Material : MaterialProps->Materials) { Key.Values.Add(Material); } return Key; } void FEditMeshMaterials_MaterialSetChange::Apply(UObject* Object) { UEditMeshMaterialsTool* Tool = CastChecked(Object); Tool->ExternalUpdateMaterialSet(MaterialsAfter); } void FEditMeshMaterials_MaterialSetChange::Revert(UObject* Object) { UEditMeshMaterialsTool* Tool = CastChecked(Object); Tool->ExternalUpdateMaterialSet(MaterialsBefore); } FString FEditMeshMaterials_MaterialSetChange::ToString() const { return FString(TEXT("MaterialSet Change")); } #undef LOCTEXT_NAMESPACE