Files
integration/Plugins/BlueprintMCP/Deprecated/UMCPHandler_DescribeMaterialInEnglish.h
2026-03-13 14:26:04 -04:00

189 lines
6.6 KiB
C++

#pragma once
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Materials/Material.h"
#include "Materials/MaterialExpression.h"
#include "Materials/MaterialExpressionScalarParameter.h"
#include "Materials/MaterialExpressionVectorParameter.h"
#include "Materials/MaterialExpressionTextureObjectParameter.h"
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
#include "Materials/MaterialExpressionConstant.h"
#include "Materials/MaterialExpressionConstant3Vector.h"
#include "Materials/MaterialExpressionConstant4Vector.h"
#include "Materials/MaterialExpressionTextureSample.h"
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
#include "Materials/MaterialFunction.h"
#include "MaterialGraph/MaterialGraph.h"
#include "MaterialGraph/MaterialGraphNode.h"
#include "MaterialGraph/MaterialGraphNode_Root.h"
#include "MaterialGraph/MaterialGraphSchema.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "UMCPHandler_DescribeMaterialInEnglish.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS(meta=(Group="Unclassified"))
class UMCPHandler_DescribeMaterialInEnglish : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Material name or package path"))
FString Material;
virtual FString GetDescription() const override
{
return TEXT("Generate a human-readable description of a material by tracing its expression graph from the root node inputs.");
}
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{
MCPAssets<UMaterial> Assets;
if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
UMaterial* MaterialObj = Assets.Object();
// Ensure material graph is built
MCPUtils::EnsureMaterialGraph(MaterialObj);
if (!MaterialObj->MaterialGraph)
{
MCPErrorCallback(Result).SetError(TEXT("Could not build MaterialGraph for this material"));
return;
}
// Find root node
UMaterialGraphNode_Root* RootNode = nullptr;
for (UEdGraphNode* Node : MaterialObj->MaterialGraph->Nodes)
{
RootNode = Cast<UMaterialGraphNode_Root>(Node);
if (RootNode) break;
}
if (!RootNode)
{
MCPErrorCallback(Result).SetError(TEXT("Could not find root node in material graph"));
return;
}
// Recursive helper: trace backwards from a pin and build a description string
TFunction<FString(UEdGraphPin*, int32)> TracePin = [&TracePin](UEdGraphPin* Pin, int32 Depth) -> FString
{
if (!Pin || Depth > 10)
return TEXT("(unknown)");
if (Pin->LinkedTo.Num() == 0)
{
if (!Pin->DefaultValue.IsEmpty())
return FString::Printf(TEXT("(default: %s)"), *Pin->DefaultValue);
return TEXT("(unconnected)");
}
TArray<FString> Sources;
for (UEdGraphPin* LinkedPin : Pin->LinkedTo)
{
if (!LinkedPin || !LinkedPin->GetOwningNode()) continue;
UEdGraphNode* SourceNode = LinkedPin->GetOwningNode();
FString NodeDesc;
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(SourceNode);
if (!MatNode)
{
NodeDesc = MCPUtils::FormatName(SourceNode);
Sources.Add(NodeDesc);
continue;
}
UMaterialExpression* Expr = MatNode->MaterialExpression;
if (!Expr)
{
NodeDesc = TEXT("(null expression)");
}
else if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr))
{
NodeDesc = FString::Printf(TEXT("ScalarParam \"%s\" (default: %.4f)"), *SP->ParameterName.ToString(), SP->DefaultValue);
}
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
{
NodeDesc = FString::Printf(TEXT("VectorParam \"%s\" (default: R=%.2f G=%.2f B=%.2f A=%.2f)"),
*VP->ParameterName.ToString(), VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A);
}
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
{
FString TexName = TP->Texture ? MCPUtils::FormatName(TP->Texture) : TEXT("None");
NodeDesc = FString::Printf(TEXT("TextureParam \"%s\" (%s)"), *TP->ParameterName.ToString(), *TexName);
}
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
{
NodeDesc = FString::Printf(TEXT("StaticSwitchParam \"%s\" (default: %s)"),
*SSP->ParameterName.ToString(), SSP->DefaultValue ? TEXT("true") : TEXT("false"));
}
else if (auto* SC = Cast<UMaterialExpressionConstant>(Expr))
{
NodeDesc = FString::Printf(TEXT("Constant(%.4f)"), SC->R);
}
else if (auto* C3 = Cast<UMaterialExpressionConstant3Vector>(Expr))
{
NodeDesc = FString::Printf(TEXT("Constant3(R=%.2f G=%.2f B=%.2f)"), C3->Constant.R, C3->Constant.G, C3->Constant.B);
}
else if (auto* C4 = Cast<UMaterialExpressionConstant4Vector>(Expr))
{
NodeDesc = FString::Printf(TEXT("Constant4(R=%.2f G=%.2f B=%.2f A=%.2f)"), C4->Constant.R, C4->Constant.G, C4->Constant.B, C4->Constant.A);
}
else if (auto* TS = Cast<UMaterialExpressionTextureSample>(Expr))
{
FString TexName = TS->Texture ? MCPUtils::FormatName(TS->Texture) : TEXT("None");
NodeDesc = FString::Printf(TEXT("TextureSample(%s)"), *TexName);
}
else if (auto* MFC = Cast<UMaterialExpressionMaterialFunctionCall>(Expr))
{
FString FuncName = MFC->MaterialFunction ? MFC->MaterialFunction->GetPathName() : TEXT("None");
NodeDesc = FString::Printf(TEXT("FunctionCall(%s)"), *FuncName);
}
else
{
NodeDesc = MCPUtils::FormatName(Expr);
}
// Recurse into input pins
TArray<FString> InputDescs;
for (UEdGraphPin* InputPin : SourceNode->Pins)
{
if (!InputPin || InputPin->Direction != EGPD_Input || InputPin->LinkedTo.Num() == 0) continue;
InputDescs.Add(TracePin(InputPin, Depth + 1));
}
if (InputDescs.Num() > 0)
{
NodeDesc += TEXT(" <- (") + FString::Join(InputDescs, TEXT(", ")) + TEXT(")");
}
Sources.Add(NodeDesc);
}
if (Sources.Num() == 1)
return Sources[0];
return TEXT("(") + FString::Join(Sources, TEXT(", ")) + TEXT(")");
};
// Trace each connected root input and output plain text
Result.Appendf(TEXT("Material: %s\n\n"), *MCPUtils::FormatName(MaterialObj));
for (UEdGraphPin* Pin : RootNode->Pins)
{
if (!Pin || Pin->Direction != EGPD_Input) continue;
if (Pin->LinkedTo.Num() == 0) continue;
FString PinName = MCPUtils::FormatName(Pin);
FString Description = TracePin(Pin, 0);
Result.Appendf(TEXT("%s <- %s\n"), *PinName, *Description);
}
}
};