General refactoring
This commit is contained in:
BIN
Content/Testing/M_Test.uasset
LFS
BIN
Content/Testing/M_Test.uasset
LFS
Binary file not shown.
450
Plugins/BlueprintMCP/Deprecated/BlueprintSerialization.cpp
Normal file
450
Plugins/BlueprintMCP/Deprecated/BlueprintSerialization.cpp
Normal 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;
|
||||
}
|
||||
@@ -37,13 +37,13 @@ public:
|
||||
F.Walk(Path);
|
||||
if (!F.Ok()) return;
|
||||
|
||||
if (UEdGraph* Graph = Cast<UEdGraph>(F.Obj))
|
||||
if (UEdGraph* Graph = Cast<UEdGraph>(F.GetObj()))
|
||||
{
|
||||
EmitGraph(Graph, Result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (UBlueprint* BP = Cast<UBlueprint>(F.Obj))
|
||||
if (UBlueprint* BP = Cast<UBlueprint>(F.GetObj()))
|
||||
{
|
||||
TArray<UEdGraph*> Graphs = MCPUtils::AllGraphs(BP);
|
||||
for (UEdGraph* Graph : Graphs)
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (UMaterial* Mat = Cast<UMaterial>(F.Obj))
|
||||
if (UMaterial* Mat = Cast<UMaterial>(F.GetObj()))
|
||||
{
|
||||
MCPUtils::EnsureMaterialGraph(Mat);
|
||||
if (!Mat->MaterialGraph)
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
}
|
||||
|
||||
Result.Appendf(TEXT("ERROR: Expected a blueprint, material, or graph, got %s\n"),
|
||||
*MCPUtils::FormatName(F.Obj->GetClass()));
|
||||
*MCPUtils::FormatName(F.GetObj()->GetClass()));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Materials/Material.h"
|
||||
#include "MaterialGraph/MaterialGraph.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "IMaterialEditor.h"
|
||||
#include "Engine/LevelScriptBlueprint.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("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("matexp"), TEXT("Get the UMaterialExpression from a UMaterialGraphNode"), &MCPFetcher::MatExp },
|
||||
};
|
||||
return Table;
|
||||
}
|
||||
@@ -100,12 +100,28 @@ MCPFetcher& MCPFetcher::Asset(const FString& PackagePath)
|
||||
if (!Editor)
|
||||
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))
|
||||
SetObj(MCPUtils::ReplaceMaterialWithTransientCopy(Mat));
|
||||
{
|
||||
IMaterialEditor *MatEditor = static_cast<IMaterialEditor*>(Editor);
|
||||
SetObj(MatEditor->GetMaterialInterface()->GetBaseMaterial());
|
||||
}
|
||||
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)
|
||||
{
|
||||
for (const FWalker& W : GetWalkerTable())
|
||||
@@ -314,18 +330,3 @@ MCPFetcher& MCPFetcher::ToGraph()
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -540,296 +540,11 @@ bool MCPUtils::SaveBlueprintPackage(UBlueprint* BP)
|
||||
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
|
||||
// ============================================================
|
||||
|
||||
|
||||
UClass* MCPUtils::FindClassByName(const FString& ClassName)
|
||||
{
|
||||
// Exact match first (handles both C++ classes and Blueprint _C classes)
|
||||
@@ -1274,125 +989,6 @@ bool MCPUtils::SaveGenericPackage(UObject* Asset)
|
||||
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
|
||||
|
||||
@@ -28,13 +28,6 @@ class IAssetEditorInstance;
|
||||
class MCPFetcher
|
||||
{
|
||||
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, UObject* O) : Obj(O), ErrorCB(CB) {}
|
||||
|
||||
@@ -47,7 +40,6 @@ public:
|
||||
MCPFetcher& Pin(const FString& Value);
|
||||
MCPFetcher& Component(const FString& Value);
|
||||
MCPFetcher& LevelBlueprint(const FString& Value);
|
||||
MCPFetcher& MatExp(const FString& Value);
|
||||
|
||||
// Parse string and walk multiple steps.
|
||||
MCPFetcher& Walk(const FString& Path);
|
||||
@@ -72,28 +64,17 @@ public:
|
||||
static const TArray<FWalker>& GetWalkerTable();
|
||||
|
||||
bool Ok() const { return !bError; }
|
||||
UObject* GetObj() const { return Obj; }
|
||||
UObject* GetAsset() const { return OriginalAsset; }
|
||||
template<class T> T* CastAsset()
|
||||
{
|
||||
if (bError) return nullptr;
|
||||
T* Result = ::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;
|
||||
if (!CheckAssetIsA(T::StaticClass())) return nullptr;
|
||||
return ::Cast<T>(OriginalAsset);
|
||||
}
|
||||
template<class AssetType, class EditorType>
|
||||
EditorType* CastEditor()
|
||||
{
|
||||
if (bError) 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;
|
||||
}
|
||||
if (!CheckAssetIsA(AssetType::StaticClass())) return nullptr;
|
||||
return static_cast<EditorType*>(Editor);
|
||||
}
|
||||
const TArray<UObject*>& Visited() const { return Chain; }
|
||||
@@ -110,6 +91,12 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
bool bError = false;
|
||||
UObject* Obj = nullptr;
|
||||
UObject* OriginalAsset = nullptr;
|
||||
IAssetEditorInstance* Editor = nullptr;
|
||||
UEdGraphPin* ResultPin = nullptr;
|
||||
MCPErrorCallback ErrorCB = nullptr;
|
||||
TArray<UObject*> Chain;
|
||||
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; }
|
||||
@@ -117,6 +104,7 @@ private:
|
||||
MCPFetcher& SetError(const FString& Msg);
|
||||
MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected);
|
||||
const FWalker* GetWalker(const FString& Key);
|
||||
bool CheckAssetIsA(UClass* StaticClass);
|
||||
};
|
||||
|
||||
template<> inline UEdGraphPin* MCPFetcher::Cast<UEdGraphPin>()
|
||||
|
||||
@@ -251,12 +251,6 @@ public:
|
||||
static UEdGraphNode* FindNodeByGuid(UBlueprint* BP, const FString& GuidString, UEdGraph** OutGraph = nullptr);
|
||||
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 -----
|
||||
static UClass* FindClassByName(const FString& ClassName);
|
||||
static bool ResolveTypeFromString(const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error);
|
||||
|
||||
Reference in New Issue
Block a user