#pragma once #include "CoreMinimal.h" #include "MCPHandler.h" #include "MCPAssets.h" #include "MCPUtils.h" #include "Materials/Material.h" #include "Materials/MaterialExpression.h" #include "Materials/MaterialFunction.h" #include "MaterialGraph/MaterialGraph.h" #include "MaterialGraph/MaterialGraphNode.h" #include "UMCPHandler_DeleteMaterialExpression.generated.h" // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- UCLASS(meta=(Group="Unclassified")) class UMCPHandler_DeleteMaterialExpression : public UObject, public IMCPHandler { GENERATED_BODY() public: UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)")) FString Material; UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)")) FString MaterialFunction; UPROPERTY(meta=(Description="Expression name (use FormatName from DumpMaterial output)")) FString Node; UPROPERTY(meta=(Optional, Description="If true, preview the change without applying it")) bool DryRun = false; virtual FString GetDescription() const override { return TEXT("Remove an expression node from a material or material function graph."); } virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override { if (Material.IsEmpty() && MaterialFunction.IsEmpty()) { MCPErrorCallback(Result).SetError(TEXT("Specify 'material' or 'materialFunction'.")); return; } // Load material or material function UMaterial* MaterialObj = nullptr; UMaterialFunction* MatFunc = nullptr; if (!MaterialFunction.IsEmpty()) { MCPAssets MFAssets; if (!MFAssets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return; MatFunc = MFAssets.Object(); } else { MCPAssets MatAssets; if (!MatAssets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; MaterialObj = MatAssets.Object(); } if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj); UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr); if (!Graph) { MCPErrorCallback(Result).SetError(TEXT("Asset has no material graph.")); return; } // Find node by name UMaterialGraphNode* TargetMatNode = nullptr; for (UEdGraphNode* GraphNode : Graph->Nodes) { if (!GraphNode) continue; UMaterialGraphNode* MatNode = Cast(GraphNode); if (!MatNode || !MatNode->MaterialExpression) continue; if (MCPUtils::Identifies(Node, MatNode->MaterialExpression)) { TargetMatNode = MatNode; break; } } if (!TargetMatNode) { MCPErrorCallback(Result).SetError(FString::Printf(TEXT("Expression '%s' not found."), *Node)); return; } FString ExprName = MCPUtils::FormatName(TargetMatNode->MaterialExpression); if (DryRun) { Result.Appendf(TEXT("DryRun: would delete %s\n"), *ExprName); return; } // Remove the expression UMaterialExpression* ExprToRemove = TargetMatNode->MaterialExpression; if (MaterialObj) MaterialObj->GetExpressionCollection().RemoveExpression(ExprToRemove); else MatFunc->GetExpressionCollection().RemoveExpression(ExprToRemove); ExprToRemove->MarkAsGarbage(); // Rebuild graph Graph->NotifyGraphChanged(); UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc; Asset->PreEditChange(nullptr); Asset->PostEditChange(); Asset->MarkPackageDirty(); // Save bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc); Result.Appendf(TEXT("Deleted %s"), *ExprName); if (!bSaved) Result.Append(TEXT(" (save failed)")); Result.Append(TEXT("\n")); } };