General refactoring

This commit is contained in:
2026-03-13 01:36:41 -04:00
parent 7202ef9bf6
commit 39fc8c6af8
7 changed files with 487 additions and 458 deletions

Binary file not shown.

View File

@@ -0,0 +1,450 @@
#include "MCPUtils.h"
#include "Dom/JsonValue.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraph/EdGraphPin.h"
#include "K2Node_CallFunction.h"
#include "K2Node_Event.h"
#include "K2Node_CustomEvent.h"
#include "K2Node_FunctionEntry.h"
#include "K2Node_EditablePinBase.h"
#include "K2Node_VariableGet.h"
#include "K2Node_VariableSet.h"
#include "K2Node_MacroInstance.h"
#include "K2Node_DynamicCast.h"
#include "K2Node_CallParentFunction.h"
#include "K2Node_IfThenElse.h"
// Animation Blueprint support
#include "Animation/AnimBlueprint.h"
#include "AnimGraphNode_StateMachine.h"
#include "AnimGraphNode_AssetPlayerBase.h"
#include "AnimGraphNode_SequencePlayer.h"
#include "AnimGraphNode_BlendSpacePlayer.h"
#include "AnimGraphNode_Base.h"
#include "AnimStateNode.h"
#include "AnimStateTransitionNode.h"
#include "AnimStateConduitNode.h"
#include "AnimStateEntryNode.h"
#include "AnimationStateMachineGraph.h"
// Material support
#include "Materials/MaterialExpression.h"
#include "Materials/MaterialExpressionScalarParameter.h"
#include "Materials/MaterialExpressionVectorParameter.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/MaterialExpressionTextureCoordinate.h"
#include "Materials/MaterialExpressionComponentMask.h"
#include "Materials/MaterialExpressionCustom.h"
#include "Materials/MaterialExpressionFunctionInput.h"
#include "Materials/MaterialExpressionFunctionOutput.h"
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
#include "MaterialGraph/MaterialGraphNode.h"
TSharedRef<FJsonObject> MCPUtils::SerializeBlueprint(UBlueprint* BP)
{
TSharedRef<FJsonObject> J = MakeShared<FJsonObject>();
J->SetStringField(TEXT("name"), BP->GetName());
J->SetStringField(TEXT("path"), BP->GetPackage()->GetName());
J->SetStringField(TEXT("parentClass"), BP->ParentClass ? BP->ParentClass->GetName() : TEXT("None"));
J->SetStringField(TEXT("blueprintType"),
StaticEnum<EBlueprintType>()->GetNameStringByValue((int64)BP->BlueprintType));
// Animation Blueprint detection
if (UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP))
{
J->SetBoolField(TEXT("isAnimBlueprint"), true);
if (AnimBP->TargetSkeleton)
{
J->SetStringField(TEXT("targetSkeleton"), AnimBP->TargetSkeleton->GetName());
J->SetStringField(TEXT("targetSkeletonPath"), AnimBP->TargetSkeleton->GetPathName());
}
}
// Variables
TArray<TSharedPtr<FJsonValue>> Vars;
for (const FBPVariableDescription& V : BP->NewVariables)
{
TSharedRef<FJsonObject> VJ = MakeShared<FJsonObject>();
VJ->SetStringField(TEXT("name"), V.VarName.ToString());
VJ->SetStringField(TEXT("type"), V.VarType.PinCategory.ToString());
if (V.VarType.PinSubCategoryObject.IsValid())
VJ->SetStringField(TEXT("subtype"), V.VarType.PinSubCategoryObject->GetName());
VJ->SetBoolField(TEXT("isArray"), V.VarType.IsArray());
VJ->SetBoolField(TEXT("isSet"), V.VarType.IsSet());
VJ->SetBoolField(TEXT("isMap"), V.VarType.IsMap());
VJ->SetStringField(TEXT("category"), V.Category.ToString());
VJ->SetStringField(TEXT("defaultValue"), V.DefaultValue);
Vars.Add(MakeShared<FJsonValueObject>(VJ));
}
J->SetArrayField(TEXT("variables"), Vars);
// Interfaces
TArray<TSharedPtr<FJsonValue>> Ifaces;
for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces)
{
if (I.Interface)
Ifaces.Add(MakeShared<FJsonValueString>(I.Interface->GetName()));
}
J->SetArrayField(TEXT("interfaces"), Ifaces);
return J;
}
TSharedPtr<FJsonObject> MCPUtils::SerializeNode(UEdGraphNode* Node)
{
TSharedRef<FJsonObject> NJ = MakeShared<FJsonObject>();
NJ->SetStringField(TEXT("id"), Node->NodeGuid.ToString());
NJ->SetStringField(TEXT("class"), Node->GetClass()->GetName());
NJ->SetStringField(TEXT("title"), Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString());
if (!Node->NodeComment.IsEmpty())
NJ->SetStringField(TEXT("comment"), Node->NodeComment);
NJ->SetNumberField(TEXT("posX"), Node->NodePosX);
NJ->SetNumberField(TEXT("posY"), Node->NodePosY);
// Material graph node — extract UMaterialExpression data
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("MaterialExpression"));
if (MatNode->MaterialExpression)
{
TSharedPtr<FJsonObject> ExprJson = SerializeMaterialExpression(MatNode->MaterialExpression);
if (ExprJson.IsValid())
{
NJ->SetObjectField(TEXT("expression"), ExprJson);
}
}
}
// Animation Blueprint node types
else if (auto* SMNode = Cast<UAnimGraphNode_StateMachine>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateMachine"));
if (SMNode->EditorStateMachineGraph)
{
NJ->SetStringField(TEXT("stateMachineName"), SMNode->EditorStateMachineGraph->GetName());
int32 StateCount = 0, TransitionCount = 0;
for (UEdGraphNode* SubNode : SMNode->EditorStateMachineGraph->Nodes)
{
if (Cast<UAnimStateNode>(SubNode)) StateCount++;
else if (Cast<UAnimStateTransitionNode>(SubNode)) TransitionCount++;
}
NJ->SetNumberField(TEXT("stateCount"), StateCount);
NJ->SetNumberField(TEXT("transitionCount"), TransitionCount);
}
}
else if (auto* SeqPlayer = Cast<UAnimGraphNode_SequencePlayer>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimSequencePlayer"));
if (UAnimationAsset* Asset = SeqPlayer->GetAnimationAsset())
{
NJ->SetStringField(TEXT("animationAsset"), Asset->GetName());
NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName());
}
}
else if (auto* BSPlayer = Cast<UAnimGraphNode_BlendSpacePlayer>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimBlendSpacePlayer"));
if (UAnimationAsset* Asset = BSPlayer->GetAnimationAsset())
{
NJ->SetStringField(TEXT("blendSpaceAsset"), Asset->GetName());
NJ->SetStringField(TEXT("blendSpaceAssetPath"), Asset->GetPathName());
}
}
else if (auto* AssetPlayer = Cast<UAnimGraphNode_AssetPlayerBase>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimAssetPlayer"));
if (UAnimationAsset* Asset = AssetPlayer->GetAnimationAsset())
{
NJ->SetStringField(TEXT("animationAsset"), Asset->GetName());
NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName());
}
}
else if (Cast<UAnimGraphNode_Base>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimNode"));
}
else if (auto* StateNode = Cast<UAnimStateNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimState"));
NJ->SetStringField(TEXT("stateName"), StateNode->GetStateName());
NJ->SetBoolField(TEXT("bAlwaysResetOnEntry"), StateNode->bAlwaysResetOnEntry);
}
else if (auto* TransNode = Cast<UAnimStateTransitionNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimTransition"));
if (UAnimStateNode* FromState = Cast<UAnimStateNode>(TransNode->GetPreviousState()))
{
NJ->SetStringField(TEXT("fromState"), FromState->GetStateName());
}
if (UAnimStateNode* ToState = Cast<UAnimStateNode>(TransNode->GetNextState()))
{
NJ->SetStringField(TEXT("toState"), ToState->GetStateName());
}
NJ->SetNumberField(TEXT("crossfadeDuration"), TransNode->CrossfadeDuration);
NJ->SetNumberField(TEXT("blendMode"), (int32)TransNode->BlendMode);
NJ->SetNumberField(TEXT("priorityOrder"), TransNode->PriorityOrder);
NJ->SetNumberField(TEXT("logicType"), (int32)TransNode->LogicType.GetValue());
NJ->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional);
}
else if (Cast<UAnimStateConduitNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimConduit"));
}
else if (Cast<UAnimStateEntryNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateEntry"));
}
// K2Node specifics — check CallParentFunction before CallFunction (inheritance)
else if (auto* CPF = Cast<UK2Node_CallParentFunction>(Node))
{
NJ->SetStringField(TEXT("functionName"), CPF->FunctionReference.GetMemberName().ToString());
if (CPF->FunctionReference.GetMemberParentClass())
NJ->SetStringField(TEXT("targetClass"), CPF->FunctionReference.GetMemberParentClass()->GetName());
NJ->SetStringField(TEXT("nodeType"), TEXT("CallParentFunction"));
}
else if (auto* CF = Cast<UK2Node_CallFunction>(Node))
{
NJ->SetStringField(TEXT("functionName"), CF->FunctionReference.GetMemberName().ToString());
if (CF->FunctionReference.GetMemberParentClass())
NJ->SetStringField(TEXT("targetClass"), CF->FunctionReference.GetMemberParentClass()->GetName());
}
else if (auto* FE = Cast<UK2Node_FunctionEntry>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("FunctionEntry"));
// Serialize UserDefinedPins (parameter names and types)
TArray<TSharedPtr<FJsonValue>> ParamArr;
for (const TSharedPtr<FUserPinInfo>& PinInfo : FE->UserDefinedPins)
{
if (!PinInfo.IsValid()) continue;
TSharedRef<FJsonObject> ParamJ = MakeShared<FJsonObject>();
ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString());
FString ParamType = PinInfo->PinType.PinCategory.ToString();
ParamJ->SetStringField(TEXT("type"), ParamType);
if (PinInfo->PinType.PinSubCategoryObject.IsValid())
ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName());
else if (ParamType == TEXT("None") || ParamType.IsEmpty())
ParamJ->SetBoolField(TEXT("typeUnknown"), true);
ParamArr.Add(MakeShared<FJsonValueObject>(ParamJ));
}
NJ->SetArrayField(TEXT("parameters"), ParamArr);
}
else if (auto* Ev = Cast<UK2Node_Event>(Node))
{
NJ->SetStringField(TEXT("eventName"), Ev->EventReference.GetMemberName().ToString());
NJ->SetStringField(TEXT("nodeType"), Ev->bOverrideFunction ? TEXT("OverrideEvent") : TEXT("Event"));
}
else if (auto* CE = Cast<UK2Node_CustomEvent>(Node))
{
NJ->SetStringField(TEXT("eventName"), CE->CustomFunctionName.ToString());
NJ->SetStringField(TEXT("nodeType"), TEXT("CustomEvent"));
// Serialize UserDefinedPins (parameter names and types)
TArray<TSharedPtr<FJsonValue>> ParamArr;
for (const TSharedPtr<FUserPinInfo>& PinInfo : CE->UserDefinedPins)
{
if (!PinInfo.IsValid()) continue;
TSharedRef<FJsonObject> ParamJ = MakeShared<FJsonObject>();
ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString());
FString ParamType = PinInfo->PinType.PinCategory.ToString();
ParamJ->SetStringField(TEXT("type"), ParamType);
if (PinInfo->PinType.PinSubCategoryObject.IsValid())
ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName());
else if (ParamType == TEXT("None") || ParamType.IsEmpty())
ParamJ->SetBoolField(TEXT("typeUnknown"), true);
ParamArr.Add(MakeShared<FJsonValueObject>(ParamJ));
}
NJ->SetArrayField(TEXT("parameters"), ParamArr);
}
else if (auto* VG = Cast<UK2Node_VariableGet>(Node))
{
NJ->SetStringField(TEXT("variableName"), VG->GetVarName().ToString());
NJ->SetStringField(TEXT("nodeType"), TEXT("VariableGet"));
}
else if (auto* VS = Cast<UK2Node_VariableSet>(Node))
{
NJ->SetStringField(TEXT("variableName"), VS->GetVarName().ToString());
NJ->SetStringField(TEXT("nodeType"), TEXT("VariableSet"));
}
else if (auto* MI = Cast<UK2Node_MacroInstance>(Node))
{
if (MI->GetMacroGraph())
NJ->SetStringField(TEXT("macroName"), MI->GetMacroGraph()->GetName());
NJ->SetStringField(TEXT("nodeType"), TEXT("MacroInstance"));
}
else if (auto* DC = Cast<UK2Node_DynamicCast>(Node))
{
if (DC->TargetType)
NJ->SetStringField(TEXT("castTarget"), DC->TargetType->GetName());
NJ->SetStringField(TEXT("nodeType"), TEXT("DynamicCast"));
}
else if (Cast<UK2Node_IfThenElse>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("Branch"));
}
// Pins
TArray<TSharedPtr<FJsonValue>> Pins;
for (UEdGraphPin* Pin : Node->Pins)
{
if (!Pin || Pin->bHidden) continue;
TSharedPtr<FJsonObject> PJ = SerializePin(Pin);
if (PJ.IsValid())
Pins.Add(MakeShared<FJsonValueObject>(PJ.ToSharedRef()));
}
NJ->SetArrayField(TEXT("pins"), Pins);
return NJ;
}
TSharedPtr<FJsonObject> MCPUtils::SerializePin(UEdGraphPin* Pin)
{
TSharedRef<FJsonObject> PJ = MakeShared<FJsonObject>();
PJ->SetStringField(TEXT("name"), Pin->PinName.ToString());
PJ->SetStringField(TEXT("direction"), Pin->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output"));
PJ->SetStringField(TEXT("type"), Pin->PinType.PinCategory.ToString());
if (Pin->PinType.PinSubCategoryObject.IsValid())
PJ->SetStringField(TEXT("subtype"), Pin->PinType.PinSubCategoryObject->GetName());
if (!Pin->DefaultValue.IsEmpty())
PJ->SetStringField(TEXT("defaultValue"), Pin->DefaultValue);
if (Pin->LinkedTo.Num() > 0)
{
TArray<TSharedPtr<FJsonValue>> Conns;
for (UEdGraphPin* Linked : Pin->LinkedTo)
{
if (!Linked || !Linked->GetOwningNode()) continue;
TSharedRef<FJsonObject> CJ = MakeShared<FJsonObject>();
CJ->SetStringField(TEXT("nodeId"), Linked->GetOwningNode()->NodeGuid.ToString());
CJ->SetStringField(TEXT("pinName"), Linked->PinName.ToString());
Conns.Add(MakeShared<FJsonValueObject>(CJ));
}
PJ->SetArrayField(TEXT("connections"), Conns);
}
return PJ;
}
TSharedPtr<FJsonObject> MCPUtils::SerializeMaterialExpression(UMaterialExpression* Expression)
{
if (!Expression) return nullptr;
TSharedRef<FJsonObject> EJ = MakeShared<FJsonObject>();
EJ->SetStringField(TEXT("class"), Expression->GetClass()->GetName());
EJ->SetStringField(TEXT("name"), Expression->GetName());
EJ->SetStringField(TEXT("description"), Expression->GetDescription());
EJ->SetNumberField(TEXT("posX"), Expression->MaterialExpressionEditorX);
EJ->SetNumberField(TEXT("posY"), Expression->MaterialExpressionEditorY);
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("ScalarParameter"));
EJ->SetStringField(TEXT("parameterName"), SP->ParameterName.ToString());
EJ->SetNumberField(TEXT("defaultValue"), SP->DefaultValue);
EJ->SetStringField(TEXT("group"), SP->Group.ToString());
}
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("VectorParameter"));
EJ->SetStringField(TEXT("parameterName"), VP->ParameterName.ToString());
TSharedRef<FJsonObject> DefVal = MakeShared<FJsonObject>();
DefVal->SetNumberField(TEXT("r"), VP->DefaultValue.R);
DefVal->SetNumberField(TEXT("g"), VP->DefaultValue.G);
DefVal->SetNumberField(TEXT("b"), VP->DefaultValue.B);
DefVal->SetNumberField(TEXT("a"), VP->DefaultValue.A);
EJ->SetObjectField(TEXT("defaultValue"), DefVal);
EJ->SetStringField(TEXT("group"), VP->Group.ToString());
}
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSampleParameter2D"));
EJ->SetStringField(TEXT("parameterName"), TP->ParameterName.ToString());
if (TP->Texture)
EJ->SetStringField(TEXT("texture"), TP->Texture->GetPathName());
EJ->SetStringField(TEXT("group"), TP->Group.ToString());
}
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("StaticSwitchParameter"));
EJ->SetStringField(TEXT("parameterName"), SSP->ParameterName.ToString());
EJ->SetBoolField(TEXT("defaultValue"), SSP->DefaultValue);
EJ->SetStringField(TEXT("group"), SSP->Group.ToString());
}
else if (auto* SC = Cast<UMaterialExpressionConstant>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("Constant"));
EJ->SetNumberField(TEXT("value"), SC->R);
}
else if (auto* C3 = Cast<UMaterialExpressionConstant3Vector>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("Constant3Vector"));
TSharedRef<FJsonObject> Val = MakeShared<FJsonObject>();
Val->SetNumberField(TEXT("r"), C3->Constant.R);
Val->SetNumberField(TEXT("g"), C3->Constant.G);
Val->SetNumberField(TEXT("b"), C3->Constant.B);
EJ->SetObjectField(TEXT("value"), Val);
}
else if (auto* C4 = Cast<UMaterialExpressionConstant4Vector>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("Constant4Vector"));
TSharedRef<FJsonObject> Val = MakeShared<FJsonObject>();
Val->SetNumberField(TEXT("r"), C4->Constant.R);
Val->SetNumberField(TEXT("g"), C4->Constant.G);
Val->SetNumberField(TEXT("b"), C4->Constant.B);
Val->SetNumberField(TEXT("a"), C4->Constant.A);
EJ->SetObjectField(TEXT("value"), Val);
}
else if (auto* TS = Cast<UMaterialExpressionTextureSample>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSample"));
if (TS->Texture)
EJ->SetStringField(TEXT("texture"), TS->Texture->GetPathName());
}
else if (auto* TC = Cast<UMaterialExpressionTextureCoordinate>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("TextureCoordinate"));
EJ->SetNumberField(TEXT("coordinateIndex"), TC->CoordinateIndex);
EJ->SetNumberField(TEXT("uTiling"), TC->UTiling);
EJ->SetNumberField(TEXT("vTiling"), TC->VTiling);
}
else if (auto* CM = Cast<UMaterialExpressionComponentMask>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("ComponentMask"));
EJ->SetBoolField(TEXT("r"), CM->R != 0);
EJ->SetBoolField(TEXT("g"), CM->G != 0);
EJ->SetBoolField(TEXT("b"), CM->B != 0);
EJ->SetBoolField(TEXT("a"), CM->A != 0);
}
else if (auto* Custom = Cast<UMaterialExpressionCustom>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("Custom"));
EJ->SetStringField(TEXT("code"), Custom->Code);
EJ->SetStringField(TEXT("outputType"), StaticEnum<ECustomMaterialOutputType>()->GetNameStringByValue((int64)Custom->OutputType));
}
else if (auto* FI = Cast<UMaterialExpressionFunctionInput>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionInput"));
EJ->SetStringField(TEXT("inputName"), FI->InputName.ToString());
}
else if (auto* FO = Cast<UMaterialExpressionFunctionOutput>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionOutput"));
EJ->SetStringField(TEXT("outputName"), FO->OutputName.ToString());
}
else if (auto* MFC = Cast<UMaterialExpressionMaterialFunctionCall>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("MaterialFunctionCall"));
if (MFC->MaterialFunction)
EJ->SetStringField(TEXT("functionName"), MFC->MaterialFunction->GetName());
}
else
{
EJ->SetStringField(TEXT("expressionType"), Expression->GetClass()->GetName());
}
return EJ;
}

View File

@@ -37,13 +37,13 @@ public:
F.Walk(Path); F.Walk(Path);
if (!F.Ok()) return; if (!F.Ok()) return;
if (UEdGraph* Graph = Cast<UEdGraph>(F.Obj)) if (UEdGraph* Graph = Cast<UEdGraph>(F.GetObj()))
{ {
EmitGraph(Graph, Result); EmitGraph(Graph, Result);
return; return;
} }
if (UBlueprint* BP = Cast<UBlueprint>(F.Obj)) if (UBlueprint* BP = Cast<UBlueprint>(F.GetObj()))
{ {
TArray<UEdGraph*> Graphs = MCPUtils::AllGraphs(BP); TArray<UEdGraph*> Graphs = MCPUtils::AllGraphs(BP);
for (UEdGraph* Graph : Graphs) for (UEdGraph* Graph : Graphs)
@@ -54,7 +54,7 @@ public:
return; return;
} }
if (UMaterial* Mat = Cast<UMaterial>(F.Obj)) if (UMaterial* Mat = Cast<UMaterial>(F.GetObj()))
{ {
MCPUtils::EnsureMaterialGraph(Mat); MCPUtils::EnsureMaterialGraph(Mat);
if (!Mat->MaterialGraph) if (!Mat->MaterialGraph)
@@ -67,7 +67,7 @@ public:
} }
Result.Appendf(TEXT("ERROR: Expected a blueprint, material, or graph, got %s\n"), Result.Appendf(TEXT("ERROR: Expected a blueprint, material, or graph, got %s\n"),
*MCPUtils::FormatName(F.Obj->GetClass())); *MCPUtils::FormatName(F.GetObj()->GetClass()));
} }
private: private:

View File

@@ -10,6 +10,7 @@
#include "Materials/Material.h" #include "Materials/Material.h"
#include "MaterialGraph/MaterialGraph.h" #include "MaterialGraph/MaterialGraph.h"
#include "MaterialGraph/MaterialGraphNode.h" #include "MaterialGraph/MaterialGraphNode.h"
#include "IMaterialEditor.h"
#include "Engine/LevelScriptBlueprint.h" #include "Engine/LevelScriptBlueprint.h"
#include "Subsystems/AssetEditorSubsystem.h" #include "Subsystems/AssetEditorSubsystem.h"
@@ -40,7 +41,6 @@ const TArray<MCPFetcher::FWalker>& MCPFetcher::GetWalkerTable()
{ TEXT("pin"), TEXT("Find a named UEdGraphPin on a node"), &MCPFetcher::Pin }, { TEXT("pin"), TEXT("Find a named UEdGraphPin on a node"), &MCPFetcher::Pin },
{ TEXT("component"), TEXT("Find a named component in a Blueprint's SCS"), &MCPFetcher::Component }, { TEXT("component"), TEXT("Find a named component in a Blueprint's SCS"), &MCPFetcher::Component },
{ TEXT("levelblueprint"), TEXT("Get the level blueprint from a UWorld"), &MCPFetcher::LevelBlueprint }, { TEXT("levelblueprint"), TEXT("Get the level blueprint from a UWorld"), &MCPFetcher::LevelBlueprint },
// { TEXT("matexp"), TEXT("Get the UMaterialExpression from a UMaterialGraphNode"), &MCPFetcher::MatExp },
}; };
return Table; return Table;
} }
@@ -100,12 +100,28 @@ MCPFetcher& MCPFetcher::Asset(const FString& PackagePath)
if (!Editor) if (!Editor)
return SetError(FString::Printf(TEXT("Could not find editor instance for '%s'"), *PackagePath)); return SetError(FString::Printf(TEXT("Could not find editor instance for '%s'"), *PackagePath));
// If this is a material open in the editor, use the editor's transient copy. // If this is a material, use the editor's transient copy.
if (UMaterial* Mat = ::Cast<UMaterial>(Obj)) if (UMaterial* Mat = ::Cast<UMaterial>(Obj))
SetObj(MCPUtils::ReplaceMaterialWithTransientCopy(Mat)); {
IMaterialEditor *MatEditor = static_cast<IMaterialEditor*>(Editor);
SetObj(MatEditor->GetMaterialInterface()->GetBaseMaterial());
}
return *this; return *this;
} }
bool MCPFetcher::CheckAssetIsA(UClass* StaticClass)
{
if (bError) return false;
if (!OriginalAsset || !OriginalAsset->IsA(StaticClass))
{
SetError(FString::Printf(TEXT("Asset is %s, expected %s"),
OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"),
*StaticClass->GetName()));
return false;
}
return true;
}
const MCPFetcher::FWalker* MCPFetcher::GetWalker(const FString& Key) const MCPFetcher::FWalker* MCPFetcher::GetWalker(const FString& Key)
{ {
for (const FWalker& W : GetWalkerTable()) for (const FWalker& W : GetWalkerTable())
@@ -314,18 +330,3 @@ MCPFetcher& MCPFetcher::ToGraph()
return TypeMismatch(TEXT("ToGraph"), TEXT("Graph or Material")); return TypeMismatch(TEXT("ToGraph"), TEXT("Graph or Material"));
} }
MCPFetcher& MCPFetcher::MatExp(const FString& Value)
{
if (bError) return *this;
UMaterialGraphNode* MatNode = ::Cast<UMaterialGraphNode>(Obj);
if (!MatNode)
return TypeMismatch(TEXT("matexp"), TEXT("UMaterialGraphNode"));
if (!MatNode->MaterialExpression)
return SetError(FString::Printf(TEXT("Node '%s' has no MaterialExpression"), *MCPUtils::FormatName(MatNode)));
SetObj(MatNode->MaterialExpression);
return *this;
}

View File

@@ -540,296 +540,11 @@ bool MCPUtils::SaveBlueprintPackage(UBlueprint* BP)
return bSuccess; return bSuccess;
} }
// ============================================================
// Serialization
// ============================================================
TSharedRef<FJsonObject> MCPUtils::SerializeBlueprint(UBlueprint* BP)
{
TSharedRef<FJsonObject> J = MakeShared<FJsonObject>();
J->SetStringField(TEXT("name"), BP->GetName());
J->SetStringField(TEXT("path"), BP->GetPackage()->GetName());
J->SetStringField(TEXT("parentClass"), BP->ParentClass ? BP->ParentClass->GetName() : TEXT("None"));
J->SetStringField(TEXT("blueprintType"),
StaticEnum<EBlueprintType>()->GetNameStringByValue((int64)BP->BlueprintType));
// Animation Blueprint detection
if (UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP))
{
J->SetBoolField(TEXT("isAnimBlueprint"), true);
if (AnimBP->TargetSkeleton)
{
J->SetStringField(TEXT("targetSkeleton"), AnimBP->TargetSkeleton->GetName());
J->SetStringField(TEXT("targetSkeletonPath"), AnimBP->TargetSkeleton->GetPathName());
}
}
// Variables
TArray<TSharedPtr<FJsonValue>> Vars;
for (const FBPVariableDescription& V : BP->NewVariables)
{
TSharedRef<FJsonObject> VJ = MakeShared<FJsonObject>();
VJ->SetStringField(TEXT("name"), V.VarName.ToString());
VJ->SetStringField(TEXT("type"), V.VarType.PinCategory.ToString());
if (V.VarType.PinSubCategoryObject.IsValid())
VJ->SetStringField(TEXT("subtype"), V.VarType.PinSubCategoryObject->GetName());
VJ->SetBoolField(TEXT("isArray"), V.VarType.IsArray());
VJ->SetBoolField(TEXT("isSet"), V.VarType.IsSet());
VJ->SetBoolField(TEXT("isMap"), V.VarType.IsMap());
VJ->SetStringField(TEXT("category"), V.Category.ToString());
VJ->SetStringField(TEXT("defaultValue"), V.DefaultValue);
Vars.Add(MakeShared<FJsonValueObject>(VJ));
}
J->SetArrayField(TEXT("variables"), Vars);
// Interfaces
TArray<TSharedPtr<FJsonValue>> Ifaces;
for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces)
{
if (I.Interface)
Ifaces.Add(MakeShared<FJsonValueString>(I.Interface->GetName()));
}
J->SetArrayField(TEXT("interfaces"), Ifaces);
return J;
}
TSharedPtr<FJsonObject> MCPUtils::SerializeNode(UEdGraphNode* Node)
{
TSharedRef<FJsonObject> NJ = MakeShared<FJsonObject>();
NJ->SetStringField(TEXT("id"), Node->NodeGuid.ToString());
NJ->SetStringField(TEXT("class"), Node->GetClass()->GetName());
NJ->SetStringField(TEXT("title"), Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString());
if (!Node->NodeComment.IsEmpty())
NJ->SetStringField(TEXT("comment"), Node->NodeComment);
NJ->SetNumberField(TEXT("posX"), Node->NodePosX);
NJ->SetNumberField(TEXT("posY"), Node->NodePosY);
// Material graph node — extract UMaterialExpression data
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("MaterialExpression"));
if (MatNode->MaterialExpression)
{
TSharedPtr<FJsonObject> ExprJson = SerializeMaterialExpression(MatNode->MaterialExpression);
if (ExprJson.IsValid())
{
NJ->SetObjectField(TEXT("expression"), ExprJson);
}
}
}
// Animation Blueprint node types
else if (auto* SMNode = Cast<UAnimGraphNode_StateMachine>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateMachine"));
if (SMNode->EditorStateMachineGraph)
{
NJ->SetStringField(TEXT("stateMachineName"), SMNode->EditorStateMachineGraph->GetName());
int32 StateCount = 0, TransitionCount = 0;
for (UEdGraphNode* SubNode : SMNode->EditorStateMachineGraph->Nodes)
{
if (Cast<UAnimStateNode>(SubNode)) StateCount++;
else if (Cast<UAnimStateTransitionNode>(SubNode)) TransitionCount++;
}
NJ->SetNumberField(TEXT("stateCount"), StateCount);
NJ->SetNumberField(TEXT("transitionCount"), TransitionCount);
}
}
else if (auto* SeqPlayer = Cast<UAnimGraphNode_SequencePlayer>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimSequencePlayer"));
if (UAnimationAsset* Asset = SeqPlayer->GetAnimationAsset())
{
NJ->SetStringField(TEXT("animationAsset"), Asset->GetName());
NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName());
}
}
else if (auto* BSPlayer = Cast<UAnimGraphNode_BlendSpacePlayer>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimBlendSpacePlayer"));
if (UAnimationAsset* Asset = BSPlayer->GetAnimationAsset())
{
NJ->SetStringField(TEXT("blendSpaceAsset"), Asset->GetName());
NJ->SetStringField(TEXT("blendSpaceAssetPath"), Asset->GetPathName());
}
}
else if (auto* AssetPlayer = Cast<UAnimGraphNode_AssetPlayerBase>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimAssetPlayer"));
if (UAnimationAsset* Asset = AssetPlayer->GetAnimationAsset())
{
NJ->SetStringField(TEXT("animationAsset"), Asset->GetName());
NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName());
}
}
else if (Cast<UAnimGraphNode_Base>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimNode"));
}
else if (auto* StateNode = Cast<UAnimStateNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimState"));
NJ->SetStringField(TEXT("stateName"), StateNode->GetStateName());
NJ->SetBoolField(TEXT("bAlwaysResetOnEntry"), StateNode->bAlwaysResetOnEntry);
}
else if (auto* TransNode = Cast<UAnimStateTransitionNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimTransition"));
if (UAnimStateNode* FromState = Cast<UAnimStateNode>(TransNode->GetPreviousState()))
{
NJ->SetStringField(TEXT("fromState"), FromState->GetStateName());
}
if (UAnimStateNode* ToState = Cast<UAnimStateNode>(TransNode->GetNextState()))
{
NJ->SetStringField(TEXT("toState"), ToState->GetStateName());
}
NJ->SetNumberField(TEXT("crossfadeDuration"), TransNode->CrossfadeDuration);
NJ->SetNumberField(TEXT("blendMode"), (int32)TransNode->BlendMode);
NJ->SetNumberField(TEXT("priorityOrder"), TransNode->PriorityOrder);
NJ->SetNumberField(TEXT("logicType"), (int32)TransNode->LogicType.GetValue());
NJ->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional);
}
else if (Cast<UAnimStateConduitNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimConduit"));
}
else if (Cast<UAnimStateEntryNode>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateEntry"));
}
// K2Node specifics — check CallParentFunction before CallFunction (inheritance)
else if (auto* CPF = Cast<UK2Node_CallParentFunction>(Node))
{
NJ->SetStringField(TEXT("functionName"), CPF->FunctionReference.GetMemberName().ToString());
if (CPF->FunctionReference.GetMemberParentClass())
NJ->SetStringField(TEXT("targetClass"), CPF->FunctionReference.GetMemberParentClass()->GetName());
NJ->SetStringField(TEXT("nodeType"), TEXT("CallParentFunction"));
}
else if (auto* CF = Cast<UK2Node_CallFunction>(Node))
{
NJ->SetStringField(TEXT("functionName"), CF->FunctionReference.GetMemberName().ToString());
if (CF->FunctionReference.GetMemberParentClass())
NJ->SetStringField(TEXT("targetClass"), CF->FunctionReference.GetMemberParentClass()->GetName());
}
else if (auto* FE = Cast<UK2Node_FunctionEntry>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("FunctionEntry"));
// Serialize UserDefinedPins (parameter names and types)
TArray<TSharedPtr<FJsonValue>> ParamArr;
for (const TSharedPtr<FUserPinInfo>& PinInfo : FE->UserDefinedPins)
{
if (!PinInfo.IsValid()) continue;
TSharedRef<FJsonObject> ParamJ = MakeShared<FJsonObject>();
ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString());
FString ParamType = PinInfo->PinType.PinCategory.ToString();
ParamJ->SetStringField(TEXT("type"), ParamType);
if (PinInfo->PinType.PinSubCategoryObject.IsValid())
ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName());
else if (ParamType == TEXT("None") || ParamType.IsEmpty())
ParamJ->SetBoolField(TEXT("typeUnknown"), true);
ParamArr.Add(MakeShared<FJsonValueObject>(ParamJ));
}
NJ->SetArrayField(TEXT("parameters"), ParamArr);
}
else if (auto* Ev = Cast<UK2Node_Event>(Node))
{
NJ->SetStringField(TEXT("eventName"), Ev->EventReference.GetMemberName().ToString());
NJ->SetStringField(TEXT("nodeType"), Ev->bOverrideFunction ? TEXT("OverrideEvent") : TEXT("Event"));
}
else if (auto* CE = Cast<UK2Node_CustomEvent>(Node))
{
NJ->SetStringField(TEXT("eventName"), CE->CustomFunctionName.ToString());
NJ->SetStringField(TEXT("nodeType"), TEXT("CustomEvent"));
// Serialize UserDefinedPins (parameter names and types)
TArray<TSharedPtr<FJsonValue>> ParamArr;
for (const TSharedPtr<FUserPinInfo>& PinInfo : CE->UserDefinedPins)
{
if (!PinInfo.IsValid()) continue;
TSharedRef<FJsonObject> ParamJ = MakeShared<FJsonObject>();
ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString());
FString ParamType = PinInfo->PinType.PinCategory.ToString();
ParamJ->SetStringField(TEXT("type"), ParamType);
if (PinInfo->PinType.PinSubCategoryObject.IsValid())
ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName());
else if (ParamType == TEXT("None") || ParamType.IsEmpty())
ParamJ->SetBoolField(TEXT("typeUnknown"), true);
ParamArr.Add(MakeShared<FJsonValueObject>(ParamJ));
}
NJ->SetArrayField(TEXT("parameters"), ParamArr);
}
else if (auto* VG = Cast<UK2Node_VariableGet>(Node))
{
NJ->SetStringField(TEXT("variableName"), VG->GetVarName().ToString());
NJ->SetStringField(TEXT("nodeType"), TEXT("VariableGet"));
}
else if (auto* VS = Cast<UK2Node_VariableSet>(Node))
{
NJ->SetStringField(TEXT("variableName"), VS->GetVarName().ToString());
NJ->SetStringField(TEXT("nodeType"), TEXT("VariableSet"));
}
else if (auto* MI = Cast<UK2Node_MacroInstance>(Node))
{
if (MI->GetMacroGraph())
NJ->SetStringField(TEXT("macroName"), MI->GetMacroGraph()->GetName());
NJ->SetStringField(TEXT("nodeType"), TEXT("MacroInstance"));
}
else if (auto* DC = Cast<UK2Node_DynamicCast>(Node))
{
if (DC->TargetType)
NJ->SetStringField(TEXT("castTarget"), DC->TargetType->GetName());
NJ->SetStringField(TEXT("nodeType"), TEXT("DynamicCast"));
}
else if (Cast<UK2Node_IfThenElse>(Node))
{
NJ->SetStringField(TEXT("nodeType"), TEXT("Branch"));
}
// Pins
TArray<TSharedPtr<FJsonValue>> Pins;
for (UEdGraphPin* Pin : Node->Pins)
{
if (!Pin || Pin->bHidden) continue;
TSharedPtr<FJsonObject> PJ = SerializePin(Pin);
if (PJ.IsValid())
Pins.Add(MakeShared<FJsonValueObject>(PJ.ToSharedRef()));
}
NJ->SetArrayField(TEXT("pins"), Pins);
return NJ;
}
TSharedPtr<FJsonObject> MCPUtils::SerializePin(UEdGraphPin* Pin)
{
TSharedRef<FJsonObject> PJ = MakeShared<FJsonObject>();
PJ->SetStringField(TEXT("name"), Pin->PinName.ToString());
PJ->SetStringField(TEXT("direction"), Pin->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output"));
PJ->SetStringField(TEXT("type"), Pin->PinType.PinCategory.ToString());
if (Pin->PinType.PinSubCategoryObject.IsValid())
PJ->SetStringField(TEXT("subtype"), Pin->PinType.PinSubCategoryObject->GetName());
if (!Pin->DefaultValue.IsEmpty())
PJ->SetStringField(TEXT("defaultValue"), Pin->DefaultValue);
if (Pin->LinkedTo.Num() > 0)
{
TArray<TSharedPtr<FJsonValue>> Conns;
for (UEdGraphPin* Linked : Pin->LinkedTo)
{
if (!Linked || !Linked->GetOwningNode()) continue;
TSharedRef<FJsonObject> CJ = MakeShared<FJsonObject>();
CJ->SetStringField(TEXT("nodeId"), Linked->GetOwningNode()->NodeGuid.ToString());
CJ->SetStringField(TEXT("pinName"), Linked->PinName.ToString());
Conns.Add(MakeShared<FJsonValueObject>(CJ));
}
PJ->SetArrayField(TEXT("connections"), Conns);
}
return PJ;
}
// ============================================================ // ============================================================
// FindClassByName / ResolveTypeFromString // FindClassByName / ResolveTypeFromString
// ============================================================ // ============================================================
UClass* MCPUtils::FindClassByName(const FString& ClassName) UClass* MCPUtils::FindClassByName(const FString& ClassName)
{ {
// Exact match first (handles both C++ classes and Blueprint _C classes) // Exact match first (handles both C++ classes and Blueprint _C classes)
@@ -1274,125 +989,6 @@ bool MCPUtils::SaveGenericPackage(UObject* Asset)
return bSuccess; return bSuccess;
} }
TSharedPtr<FJsonObject> MCPUtils::SerializeMaterialExpression(UMaterialExpression* Expression)
{
if (!Expression) return nullptr;
TSharedRef<FJsonObject> EJ = MakeShared<FJsonObject>();
EJ->SetStringField(TEXT("class"), Expression->GetClass()->GetName());
EJ->SetStringField(TEXT("name"), Expression->GetName());
EJ->SetStringField(TEXT("description"), Expression->GetDescription());
EJ->SetNumberField(TEXT("posX"), Expression->MaterialExpressionEditorX);
EJ->SetNumberField(TEXT("posY"), Expression->MaterialExpressionEditorY);
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("ScalarParameter"));
EJ->SetStringField(TEXT("parameterName"), SP->ParameterName.ToString());
EJ->SetNumberField(TEXT("defaultValue"), SP->DefaultValue);
EJ->SetStringField(TEXT("group"), SP->Group.ToString());
}
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("VectorParameter"));
EJ->SetStringField(TEXT("parameterName"), VP->ParameterName.ToString());
TSharedRef<FJsonObject> DefVal = MakeShared<FJsonObject>();
DefVal->SetNumberField(TEXT("r"), VP->DefaultValue.R);
DefVal->SetNumberField(TEXT("g"), VP->DefaultValue.G);
DefVal->SetNumberField(TEXT("b"), VP->DefaultValue.B);
DefVal->SetNumberField(TEXT("a"), VP->DefaultValue.A);
EJ->SetObjectField(TEXT("defaultValue"), DefVal);
EJ->SetStringField(TEXT("group"), VP->Group.ToString());
}
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSampleParameter2D"));
EJ->SetStringField(TEXT("parameterName"), TP->ParameterName.ToString());
if (TP->Texture)
EJ->SetStringField(TEXT("texture"), TP->Texture->GetPathName());
EJ->SetStringField(TEXT("group"), TP->Group.ToString());
}
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("StaticSwitchParameter"));
EJ->SetStringField(TEXT("parameterName"), SSP->ParameterName.ToString());
EJ->SetBoolField(TEXT("defaultValue"), SSP->DefaultValue);
EJ->SetStringField(TEXT("group"), SSP->Group.ToString());
}
else if (auto* SC = Cast<UMaterialExpressionConstant>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("Constant"));
EJ->SetNumberField(TEXT("value"), SC->R);
}
else if (auto* C3 = Cast<UMaterialExpressionConstant3Vector>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("Constant3Vector"));
TSharedRef<FJsonObject> Val = MakeShared<FJsonObject>();
Val->SetNumberField(TEXT("r"), C3->Constant.R);
Val->SetNumberField(TEXT("g"), C3->Constant.G);
Val->SetNumberField(TEXT("b"), C3->Constant.B);
EJ->SetObjectField(TEXT("value"), Val);
}
else if (auto* C4 = Cast<UMaterialExpressionConstant4Vector>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("Constant4Vector"));
TSharedRef<FJsonObject> Val = MakeShared<FJsonObject>();
Val->SetNumberField(TEXT("r"), C4->Constant.R);
Val->SetNumberField(TEXT("g"), C4->Constant.G);
Val->SetNumberField(TEXT("b"), C4->Constant.B);
Val->SetNumberField(TEXT("a"), C4->Constant.A);
EJ->SetObjectField(TEXT("value"), Val);
}
else if (auto* TS = Cast<UMaterialExpressionTextureSample>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSample"));
if (TS->Texture)
EJ->SetStringField(TEXT("texture"), TS->Texture->GetPathName());
}
else if (auto* TC = Cast<UMaterialExpressionTextureCoordinate>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("TextureCoordinate"));
EJ->SetNumberField(TEXT("coordinateIndex"), TC->CoordinateIndex);
EJ->SetNumberField(TEXT("uTiling"), TC->UTiling);
EJ->SetNumberField(TEXT("vTiling"), TC->VTiling);
}
else if (auto* CM = Cast<UMaterialExpressionComponentMask>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("ComponentMask"));
EJ->SetBoolField(TEXT("r"), CM->R != 0);
EJ->SetBoolField(TEXT("g"), CM->G != 0);
EJ->SetBoolField(TEXT("b"), CM->B != 0);
EJ->SetBoolField(TEXT("a"), CM->A != 0);
}
else if (auto* Custom = Cast<UMaterialExpressionCustom>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("Custom"));
EJ->SetStringField(TEXT("code"), Custom->Code);
EJ->SetStringField(TEXT("outputType"), StaticEnum<ECustomMaterialOutputType>()->GetNameStringByValue((int64)Custom->OutputType));
}
else if (auto* FI = Cast<UMaterialExpressionFunctionInput>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionInput"));
EJ->SetStringField(TEXT("inputName"), FI->InputName.ToString());
}
else if (auto* FO = Cast<UMaterialExpressionFunctionOutput>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionOutput"));
EJ->SetStringField(TEXT("outputName"), FO->OutputName.ToString());
}
else if (auto* MFC = Cast<UMaterialExpressionMaterialFunctionCall>(Expression))
{
EJ->SetStringField(TEXT("expressionType"), TEXT("MaterialFunctionCall"));
if (MFC->MaterialFunction)
EJ->SetStringField(TEXT("functionName"), MFC->MaterialFunction->GetName());
}
else
{
EJ->SetStringField(TEXT("expressionType"), Expression->GetClass()->GetName());
}
return EJ;
}
// ============================================================ // ============================================================
// Anim blueprint helpers // Anim blueprint helpers

View File

@@ -28,13 +28,6 @@ class IAssetEditorInstance;
class MCPFetcher class MCPFetcher
{ {
public: public:
bool bError = false;
UObject* Obj = nullptr;
UObject* OriginalAsset = nullptr;
IAssetEditorInstance* Editor = nullptr;
UEdGraphPin* ResultPin = nullptr;
MCPErrorCallback ErrorCB = nullptr;
MCPFetcher(MCPErrorCallback CB) : ErrorCB(CB) {} MCPFetcher(MCPErrorCallback CB) : ErrorCB(CB) {}
MCPFetcher(MCPErrorCallback CB, UObject* O) : Obj(O), ErrorCB(CB) {} MCPFetcher(MCPErrorCallback CB, UObject* O) : Obj(O), ErrorCB(CB) {}
@@ -47,7 +40,6 @@ public:
MCPFetcher& Pin(const FString& Value); MCPFetcher& Pin(const FString& Value);
MCPFetcher& Component(const FString& Value); MCPFetcher& Component(const FString& Value);
MCPFetcher& LevelBlueprint(const FString& Value); MCPFetcher& LevelBlueprint(const FString& Value);
MCPFetcher& MatExp(const FString& Value);
// Parse string and walk multiple steps. // Parse string and walk multiple steps.
MCPFetcher& Walk(const FString& Path); MCPFetcher& Walk(const FString& Path);
@@ -72,28 +64,17 @@ public:
static const TArray<FWalker>& GetWalkerTable(); static const TArray<FWalker>& GetWalkerTable();
bool Ok() const { return !bError; } bool Ok() const { return !bError; }
UObject* GetObj() const { return Obj; }
UObject* GetAsset() const { return OriginalAsset; } UObject* GetAsset() const { return OriginalAsset; }
template<class T> T* CastAsset() template<class T> T* CastAsset()
{ {
if (bError) return nullptr; if (!CheckAssetIsA(T::StaticClass())) return nullptr;
T* Result = ::Cast<T>(OriginalAsset); return ::Cast<T>(OriginalAsset);
if (!Result)
SetError(FString::Printf(TEXT("Asset is %s, expected %s"),
OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"),
*T::StaticClass()->GetName()));
return Result;
} }
template<class AssetType, class EditorType> template<class AssetType, class EditorType>
EditorType* CastEditor() EditorType* CastEditor()
{ {
if (bError) return nullptr; if (!CheckAssetIsA(AssetType::StaticClass())) return nullptr;
if (!OriginalAsset || !OriginalAsset->IsA<AssetType>())
{
SetError(FString::Printf(TEXT("Asset is %s, expected %s"),
OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"),
*AssetType::StaticClass()->GetName()));
return nullptr;
}
return static_cast<EditorType*>(Editor); return static_cast<EditorType*>(Editor);
} }
const TArray<UObject*>& Visited() const { return Chain; } const TArray<UObject*>& Visited() const { return Chain; }
@@ -110,6 +91,12 @@ public:
private: private:
bool bError = false;
UObject* Obj = nullptr;
UObject* OriginalAsset = nullptr;
IAssetEditorInstance* Editor = nullptr;
UEdGraphPin* ResultPin = nullptr;
MCPErrorCallback ErrorCB = nullptr;
TArray<UObject*> Chain; TArray<UObject*> Chain;
static bool StrEq(const FString& A, const TCHAR* B) { return A.Equals(B, ESearchCase::IgnoreCase); } static bool StrEq(const FString& A, const TCHAR* B) { return A.Equals(B, ESearchCase::IgnoreCase); }
void SetObj(UObject* InObj) { if (InObj) Chain.AddUnique(InObj); Obj = InObj; ResultPin = nullptr; } void SetObj(UObject* InObj) { if (InObj) Chain.AddUnique(InObj); Obj = InObj; ResultPin = nullptr; }
@@ -117,6 +104,7 @@ private:
MCPFetcher& SetError(const FString& Msg); MCPFetcher& SetError(const FString& Msg);
MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected); MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected);
const FWalker* GetWalker(const FString& Key); const FWalker* GetWalker(const FString& Key);
bool CheckAssetIsA(UClass* StaticClass);
}; };
template<> inline UEdGraphPin* MCPFetcher::Cast<UEdGraphPin>() template<> inline UEdGraphPin* MCPFetcher::Cast<UEdGraphPin>()

View File

@@ -251,12 +251,6 @@ public:
static UEdGraphNode* FindNodeByGuid(UBlueprint* BP, const FString& GuidString, UEdGraph** OutGraph = nullptr); static UEdGraphNode* FindNodeByGuid(UBlueprint* BP, const FString& GuidString, UEdGraph** OutGraph = nullptr);
static bool SaveBlueprintPackage(UBlueprint* BP); static bool SaveBlueprintPackage(UBlueprint* BP);
// ----- Serialization -----
static TSharedRef<FJsonObject> SerializeBlueprint(UBlueprint* BP);
static TSharedPtr<FJsonObject> SerializeNode(UEdGraphNode* Node);
static TSharedPtr<FJsonObject> SerializePin(UEdGraphPin* Pin);
static TSharedPtr<FJsonObject> SerializeMaterialExpression(UMaterialExpression* Expression);
// ----- Type resolution ----- // ----- Type resolution -----
static UClass* FindClassByName(const FString& ClassName); static UClass* FindClassByName(const FString& ClassName);
static bool ResolveTypeFromString(const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error); static bool ResolveTypeFromString(const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error);