UE Wingman renaming complete.
This commit is contained in:
67
Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.cpp
Normal file
67
Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "BlueprintExportSubsystem.h"
|
||||
#include "BlueprintExporter.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "UObject/Package.h"
|
||||
#include "Exporters/Exporter.h"
|
||||
#include "UnrealExporter.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/Paths.h"
|
||||
|
||||
void UBlueprintExportSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
OnAssetSavedHandle = UPackage::PackageSavedWithContextEvent.AddUObject(
|
||||
this, &UBlueprintExportSubsystem::OnAssetSaved);
|
||||
}
|
||||
|
||||
void UBlueprintExportSubsystem::Deinitialize()
|
||||
{
|
||||
UPackage::PackageSavedWithContextEvent.Remove(OnAssetSavedHandle);
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
void UBlueprintExportSubsystem::OnAssetSaved(const FString& PackageFilename, UPackage* Package, FObjectPostSaveContext Context)
|
||||
{
|
||||
if (!Package) return;
|
||||
|
||||
FString PkDir = FPaths::ProjectDir() / TEXT("Saved") / TEXT("BlueprintExports") / FPaths::GetBaseFilename(PackageFilename);
|
||||
IFileManager::Get().DeleteDirectory(*PkDir, false, true);
|
||||
|
||||
// Export the whole package in both formats for comparison.
|
||||
{
|
||||
FStringOutputDevice Archive;
|
||||
const FExportObjectInnerContext InnerContext;
|
||||
UExporter::ExportToOutputDevice(&InnerContext, Package, nullptr, Archive, TEXT("copy"), 0);
|
||||
FFileHelper::SaveStringToFile(Archive, *(PkDir / TEXT("COPY_DUMP.txt")));
|
||||
}
|
||||
{
|
||||
FStringOutputDevice Archive;
|
||||
const FExportObjectInnerContext InnerContext;
|
||||
UExporter::ExportToOutputDevice(&InnerContext, Package, nullptr, Archive, TEXT("t3d"), 0);
|
||||
FFileHelper::SaveStringToFile(Archive, *(PkDir / TEXT("T3D_DUMP.txt")));
|
||||
}
|
||||
|
||||
TArray<UObject*> AllObjects;
|
||||
GetObjectsWithPackage(Package, AllObjects);
|
||||
for (UObject *Obj : AllObjects)
|
||||
{
|
||||
if (UBlueprint *BP = Cast<UBlueprint>(Obj))
|
||||
{
|
||||
FString BPDir = PkDir / BP->GetName();
|
||||
TArray<UEdGraph*> AllGraphs;
|
||||
BP->GetAllGraphs(AllGraphs);
|
||||
for (UEdGraph* Graph : AllGraphs)
|
||||
{
|
||||
MCPGraphExport Exporter(Graph);
|
||||
|
||||
FString FilePath = BPDir / Graph->GetName() + TEXT(".txt");
|
||||
FString DetailsPath = BPDir / TEXT("DETAILS") / Graph->GetName() + TEXT(".txt");
|
||||
FFileHelper::SaveStringToFile(Exporter.GetOutput(), *FilePath);
|
||||
FFileHelper::SaveStringToFile(Exporter.GetDetails(), *DetailsPath);
|
||||
UE_LOG(LogTemp, Warning, TEXT("Blueprint export: %s"), *FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.h
Normal file
23
Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "EditorSubsystem.h"
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
#include "BlueprintExportSubsystem.generated.h"
|
||||
|
||||
/**
|
||||
* Editor subsystem that exports blueprint text files whenever an asset is saved.
|
||||
*/
|
||||
UCLASS()
|
||||
class UBlueprintExportSubsystem : public UEditorSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
private:
|
||||
void OnAssetSaved(const FString& PackageFilename, UPackage* Package, FObjectPostSaveContext Context);
|
||||
FDelegateHandle OnAssetSavedHandle;
|
||||
};
|
||||
450
Plugins/UEWingman/Deprecated/BlueprintSerialization.cpp
Normal file
450
Plugins/UEWingman/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;
|
||||
}
|
||||
79
Plugins/UEWingman/Deprecated/Blueprint_Search.h
Normal file
79
Plugins/UEWingman/Deprecated/Blueprint_Search.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPServer.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "MCPTypes.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Blueprint_Search.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UMCP_Blueprint_Search : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Substring filter for blueprint name or path"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Filter by parent class name (exact match, case-insensitive)"))
|
||||
FString ParentClass;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("List all Blueprint assets in the project, with optional filtering by name, parent class, or type.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPAssets<UObject> Assets;
|
||||
Assets.Scan<UBlueprint>().Substring(Query).Limit(500);
|
||||
if (!Assets.Info()) return;
|
||||
|
||||
UClass *Parent = nullptr;
|
||||
if (!ParentClass.IsEmpty())
|
||||
{
|
||||
Parent = UMCPTypes::TextToOneObjectType(ParentClass);
|
||||
if (!Parent) return;
|
||||
}
|
||||
|
||||
int32 Count = 0;
|
||||
for (const FAssetData& Asset : Assets.AllData())
|
||||
{
|
||||
// Extract parent class name from asset tags
|
||||
FString ParentClassName;
|
||||
Asset.GetTagValue(FName(TEXT("ParentClass")), ParentClassName);
|
||||
int32 DotIndex;
|
||||
if (ParentClassName.FindLastChar('.', DotIndex))
|
||||
{
|
||||
ParentClassName = ParentClassName.Mid(DotIndex + 1);
|
||||
}
|
||||
ParentClassName.RemoveFromEnd(TEXT("'"));
|
||||
|
||||
// Apply parent class filter
|
||||
if (!ParentClass.IsEmpty())
|
||||
{
|
||||
if (!ParentClassName.Equals(ParentClass, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
UMCPServer::Printf(TEXT("%30s %s\n"), *ParentClassName, *Asset.PackageName.ToString());
|
||||
Count++;
|
||||
}
|
||||
|
||||
if (Count == 0)
|
||||
{
|
||||
UMCPServer::Print(TEXT("No blueprint assets found.\n"));
|
||||
}
|
||||
}
|
||||
};
|
||||
127
Plugins/UEWingman/Deprecated/Blueprint_SearchContents.h
Normal file
127
Plugins/UEWingman/Deprecated/Blueprint_SearchContents.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPServer.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Engine/Level.h"
|
||||
#include "Engine/LevelScriptBlueprint.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "K2Node_CallFunction.h"
|
||||
#include "K2Node_Event.h"
|
||||
#include "K2Node_CustomEvent.h"
|
||||
#include "K2Node_VariableGet.h"
|
||||
#include "K2Node_VariableSet.h"
|
||||
#include "Blueprint_SearchContents.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UMCP_Blueprint_SearchContents : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Search query string to match against node titles, function names, event names, and variable names"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Filter results to blueprints whose path contains this substring"))
|
||||
FString Path;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Maximum number of results to return (default 50, max 200)"))
|
||||
int32 MaxResults = 0;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Search across all Blueprint graphs for nodes matching a query string.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
int32 Limit = (MaxResults > 0) ? FMath::Clamp(MaxResults, 1, 200) : 50;
|
||||
int32 Count = 0;
|
||||
|
||||
// Search one blueprint's nodes for the query string.
|
||||
auto SearchBlueprint = [&](UBlueprint* BP, bool bIsLevelBP)
|
||||
{
|
||||
for (UEdGraphNode* Node : MCPUtils::AllNodes(BP))
|
||||
{
|
||||
if (Count >= Limit) return;
|
||||
|
||||
FString Title = Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString();
|
||||
|
||||
FString FuncName, EventName, VarName;
|
||||
if (auto* CF = Cast<UK2Node_CallFunction>(Node))
|
||||
{
|
||||
FuncName = CF->FunctionReference.GetMemberName().ToString();
|
||||
}
|
||||
else if (auto* Ev = Cast<UK2Node_Event>(Node))
|
||||
{
|
||||
EventName = Ev->EventReference.GetMemberName().ToString();
|
||||
}
|
||||
else if (auto* CE = Cast<UK2Node_CustomEvent>(Node))
|
||||
{
|
||||
EventName = CE->CustomFunctionName.ToString();
|
||||
}
|
||||
else if (auto* VG = Cast<UK2Node_VariableGet>(Node))
|
||||
{
|
||||
VarName = VG->GetVarName().ToString();
|
||||
}
|
||||
else if (auto* VS = Cast<UK2Node_VariableSet>(Node))
|
||||
{
|
||||
VarName = VS->GetVarName().ToString();
|
||||
}
|
||||
|
||||
bool bMatch = Title.Contains(Query, ESearchCase::IgnoreCase) ||
|
||||
(!FuncName.IsEmpty() && FuncName.Contains(Query, ESearchCase::IgnoreCase)) ||
|
||||
(!EventName.IsEmpty() && EventName.Contains(Query, ESearchCase::IgnoreCase)) ||
|
||||
(!VarName.IsEmpty() && VarName.Contains(Query, ESearchCase::IgnoreCase));
|
||||
|
||||
if (!bMatch) continue;
|
||||
|
||||
Count++;
|
||||
UMCPServer::Printf(TEXT("blueprint: %s\n"), *MCPUtils::FormatName(BP));
|
||||
UMCPServer::Printf(TEXT(" graph: %s\n"), *MCPUtils::FormatName(Node->GetGraph()));
|
||||
UMCPServer::Printf(TEXT(" node: %s\n"), *MCPUtils::FormatName(Node));
|
||||
UMCPServer::Printf(TEXT(" class: %s\n"), *MCPUtils::FormatName(Node->GetClass()));
|
||||
if (!FuncName.IsEmpty()) UMCPServer::Printf(TEXT(" function: %s\n"), *FuncName);
|
||||
if (!EventName.IsEmpty()) UMCPServer::Printf(TEXT(" event: %s\n"), *EventName);
|
||||
if (!VarName.IsEmpty()) UMCPServer::Printf(TEXT(" variable: %s\n"), *VarName);
|
||||
if (bIsLevelBP) UMCPServer::Print(TEXT(" level-blueprint: true\n"));
|
||||
UMCPServer::Print(TEXT("\n"));
|
||||
}
|
||||
};
|
||||
|
||||
// Search regular blueprints
|
||||
MCPAssets<UBlueprint> AllBlueprints;
|
||||
if (!Path.IsEmpty()) AllBlueprints.Substring(Path);
|
||||
AllBlueprints.Load();
|
||||
for (UBlueprint* BP : AllBlueprints.Objects())
|
||||
{
|
||||
if (Count >= Limit) break;
|
||||
SearchBlueprint(BP, false);
|
||||
}
|
||||
|
||||
// Search level blueprints
|
||||
MCPAssets<UWorld> AllWorlds;
|
||||
if (!Path.IsEmpty()) AllWorlds.Substring(Path);
|
||||
AllWorlds.Load();
|
||||
for (UWorld* World : AllWorlds.Objects())
|
||||
{
|
||||
if (Count >= Limit) break;
|
||||
if (!World || !World->PersistentLevel) continue;
|
||||
ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(false);
|
||||
if (!LevelBP) continue;
|
||||
SearchBlueprint(LevelBP, true);
|
||||
}
|
||||
|
||||
UMCPServer::Printf(TEXT("Results: %d\n"), Count);
|
||||
if (Count >= Limit) UMCPServer::Printf(TEXT("(limit %d reached, use MaxResults to increase)\n"), Limit);
|
||||
}
|
||||
};
|
||||
95
Plugins/UEWingman/Deprecated/Material_ReparentInstance.h
Normal file
95
Plugins/UEWingman/Deprecated/Material_ReparentInstance.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "Material_ReparentInstance.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UMCP_Material_ReparentInstance : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Material Instance name or path to reparent"))
|
||||
FString MaterialInstance;
|
||||
|
||||
UPROPERTY(meta=(Description="New parent material name or path (Material or Material Instance)"))
|
||||
FString NewParent;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="If true, validate without applying changes"))
|
||||
bool DryRun = false;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Change the parent material of a Material Instance. "
|
||||
"Validates against circular parent chains.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
// Load the Material Instance
|
||||
MCPAssets<UMaterialInstanceConstant> Assets;
|
||||
if (!Assets.Exact(MaterialInstance).Errors(Result).ENone().ETwo().Load()) return;
|
||||
UMaterialInstanceConstant* MI = Assets.Object();
|
||||
|
||||
// Load new parent (Material or MaterialInstance)
|
||||
MCPAssets<UMaterialInterface> ParentAssets;
|
||||
ParentAssets.NoScans();
|
||||
ParentAssets.Scan<UMaterial>();
|
||||
ParentAssets.Scan<UMaterialInstanceConstant>();
|
||||
if (!ParentAssets.Exact(NewParent).Errors(Result).ENone().ETwo().Load()) return;
|
||||
UMaterialInterface* NewParentObj = ParentAssets.Object();
|
||||
|
||||
// Prevent circular parenting
|
||||
UMaterialInterface* Check = NewParentObj;
|
||||
while (Check)
|
||||
{
|
||||
if (Check == MI)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Reparenting to '%s' would create a circular parent chain.\n"),
|
||||
*NameOf(NewParentObj));
|
||||
return;
|
||||
}
|
||||
UMaterialInstanceConstant* CheckMI = Cast<UMaterialInstanceConstant>(Check);
|
||||
if (!CheckMI) break;
|
||||
Check = CheckMI->Parent;
|
||||
}
|
||||
|
||||
FString OldParentName = MI->Parent ? NameOf(MI->Parent) : TEXT("None");
|
||||
|
||||
if (DryRun)
|
||||
{
|
||||
Result.Appendf(TEXT("[DRY RUN] Would reparent %s: %s -> %s\n"),
|
||||
*MCPUtils::FormatName(MI), *OldParentName, *NameOf(NewParentObj));
|
||||
return;
|
||||
}
|
||||
|
||||
MCPUtils::PreEdit({MI});
|
||||
MI->Parent = NewParentObj;
|
||||
MCPUtils::PostEdit({MI});
|
||||
MCPUtils::SaveGenericPackage(MI);
|
||||
|
||||
Result.Appendf(TEXT("Reparented %s: %s -> %s\n"),
|
||||
*MCPUtils::FormatName(MI), *OldParentName, *NameOf(NewParentObj));
|
||||
}
|
||||
|
||||
private:
|
||||
FString NameOf(UMaterialInterface* Obj)
|
||||
{
|
||||
if (UMaterial* M = Cast<UMaterial>(Obj))
|
||||
return MCPUtils::FormatName(M);
|
||||
if (UMaterialInstance* MI = Cast<UMaterialInstance>(Obj))
|
||||
return MCPUtils::FormatName(MI);
|
||||
return Obj->GetPathName();
|
||||
}
|
||||
};
|
||||
169
Plugins/UEWingman/Deprecated/UMCPHandler_AddMaterialExpression.h
Normal file
169
Plugins/UEWingman/Deprecated/UMCPHandler_AddMaterialExpression.h
Normal file
@@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "MaterialGraph/MaterialGraph.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
#include "UMCPHandler_AddMaterialExpression.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_AddMaterialExpression : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction, not both)"))
|
||||
FString Material;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material, not both)"))
|
||||
FString MaterialFunction;
|
||||
|
||||
UPROPERTY(meta=(Description="Expression class name without 'MaterialExpression' prefix (e.g. 'Constant', 'ScalarParameter', 'Add', 'Multiply', 'Lerp')"))
|
||||
FString ExpressionClass;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="X position in the material graph editor"))
|
||||
int32 PosX = 0;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Y position in the material graph editor"))
|
||||
int32 PosY = 0;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Add a new expression node to a material or material function graph.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
if (Material.IsEmpty() && MaterialFunction.IsEmpty())
|
||||
{
|
||||
Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction'\n"));
|
||||
return;
|
||||
}
|
||||
if (!Material.IsEmpty() && !MaterialFunction.IsEmpty())
|
||||
{
|
||||
Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction', not both\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve the expression class
|
||||
UClass* ExprClass = ResolveExpressionClass(Result);
|
||||
if (!ExprClass) return;
|
||||
|
||||
// Load material or material function
|
||||
UMaterial* MaterialObj = nullptr;
|
||||
UMaterialFunction* MatFunc = nullptr;
|
||||
UObject* Owner = nullptr;
|
||||
|
||||
if (!MaterialFunction.IsEmpty())
|
||||
{
|
||||
MCPAssets<UMaterialFunction> Assets;
|
||||
if (!Assets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MatFunc = Assets.Object();
|
||||
Owner = MatFunc;
|
||||
}
|
||||
else
|
||||
{
|
||||
MCPAssets<UMaterial> Assets;
|
||||
if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MaterialObj = Assets.Object();
|
||||
Owner = MaterialObj;
|
||||
}
|
||||
|
||||
// Ensure the MaterialGraph exists (commandlet mode doesn't auto-create it)
|
||||
if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj);
|
||||
|
||||
// Create the expression
|
||||
UMaterialExpression* NewExpr = NewObject<UMaterialExpression>(Owner, ExprClass);
|
||||
if (!NewExpr)
|
||||
{
|
||||
Result.Append(TEXT("ERROR: Failed to create material expression object\n"));
|
||||
return;
|
||||
}
|
||||
NewExpr->MaterialExpressionEditorX = PosX;
|
||||
NewExpr->MaterialExpressionEditorY = PosY;
|
||||
|
||||
if (MaterialObj)
|
||||
{
|
||||
MaterialObj->GetExpressionCollection().AddExpression(NewExpr);
|
||||
if (MaterialObj->MaterialGraph)
|
||||
MaterialObj->MaterialGraph->RebuildGraph();
|
||||
MaterialObj->PreEditChange(nullptr);
|
||||
MaterialObj->PostEditChange();
|
||||
MaterialObj->MarkPackageDirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
MatFunc->GetExpressionCollection().AddExpression(NewExpr);
|
||||
MatFunc->PreEditChange(nullptr);
|
||||
MatFunc->PostEditChange();
|
||||
MatFunc->MarkPackageDirty();
|
||||
}
|
||||
|
||||
// Save
|
||||
bool bSaved = MaterialObj
|
||||
? MCPUtils::SaveMaterialPackage(MaterialObj)
|
||||
: MCPUtils::SaveGenericPackage(MatFunc);
|
||||
|
||||
// Output
|
||||
Result.Appendf(TEXT("Added %s\n"), *MCPUtils::FormatName(NewExpr));
|
||||
|
||||
// Find the graph node GUID (only for materials with a material graph)
|
||||
if (MaterialObj && MaterialObj->MaterialGraph)
|
||||
{
|
||||
for (UEdGraphNode* Node : MaterialObj->MaterialGraph->Nodes)
|
||||
{
|
||||
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node);
|
||||
if (MatNode && MatNode->MaterialExpression == NewExpr)
|
||||
{
|
||||
Result.Appendf(TEXT("NodeId: %s\n"), *Node->NodeGuid.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bSaved)
|
||||
Result.Append(TEXT("WARNING: Failed to save package\n"));
|
||||
}
|
||||
|
||||
private:
|
||||
UClass* ResolveExpressionClass(FStringBuilderBase& Result)
|
||||
{
|
||||
// Convenience aliases
|
||||
static TMap<FString, FString> Aliases = {
|
||||
{TEXT("Lerp"), TEXT("LinearInterpolate")},
|
||||
};
|
||||
|
||||
FString LookupName = ExpressionClass;
|
||||
if (const FString* Alias = Aliases.Find(ExpressionClass))
|
||||
LookupName = *Alias;
|
||||
|
||||
FString FullClassName = FString::Printf(TEXT("MaterialExpression%s"), *LookupName);
|
||||
for (TObjectIterator<UClass> It; It; ++It)
|
||||
{
|
||||
if (It->GetName() == FullClassName && It->IsChildOf(UMaterialExpression::StaticClass()))
|
||||
{
|
||||
if (It->HasAnyClassFlags(CLASS_Abstract))
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Expression class '%s' is abstract\n"), *ExpressionClass);
|
||||
return nullptr;
|
||||
}
|
||||
return *It;
|
||||
}
|
||||
}
|
||||
|
||||
Result.Appendf(TEXT("ERROR: Unknown expression class '%s'. Use the UMaterialExpression subclass name without the 'MaterialExpression' prefix (e.g. 'Constant', 'ScalarParameter', 'Add', 'Multiply', 'Lerp')\n"),
|
||||
*ExpressionClass);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
73
Plugins/UEWingman/Deprecated/UMCPHandler_AddStructField.h
Normal file
73
Plugins/UEWingman/Deprecated/UMCPHandler_AddStructField.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPTypes.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "StructUtils/UserDefinedStruct.h"
|
||||
#include "UserDefinedStructure/UserDefinedStructEditorData.h"
|
||||
#include "UMCPHandler_AddStructField.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_AddStructField : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Package path of the struct asset"))
|
||||
FString Struct;
|
||||
|
||||
UPROPERTY(meta=(Description="Name for the new field"))
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(meta=(Description="Type for the new field (e.g. 'int32', 'FString', 'FVector')"))
|
||||
FString Type;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Add a new field to a UserDefinedStruct asset.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
// Find the struct
|
||||
MCPFetcher F(Result);
|
||||
UUserDefinedStruct* S = F.Walk(Struct).Cast<UUserDefinedStruct>();
|
||||
if (!S) return;
|
||||
|
||||
// Resolve type
|
||||
FEdGraphPinType PinType;
|
||||
if (!UMCPTypes::TextToType(Type, PinType, Result))
|
||||
return;
|
||||
|
||||
// Snapshot existing GUIDs so we can find the newly added one
|
||||
TSet<FGuid> ExistingGuids;
|
||||
for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(S))
|
||||
ExistingGuids.Add(Var.VarGuid);
|
||||
|
||||
if (!FStructureEditorUtils::AddVariable(S, PinType))
|
||||
{
|
||||
Result.Append(TEXT("ERROR: Failed to add field to struct.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the new variable by diffing GUID sets and rename it
|
||||
for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(S))
|
||||
{
|
||||
if (!ExistingGuids.Contains(Var.VarGuid))
|
||||
{
|
||||
FStructureEditorUtils::RenameVariable(S, Var.VarGuid, Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MCPUtils::SaveGenericPackage(S);
|
||||
Result.Appendf(TEXT("Added field: %s %s\n"), *Type, *Name);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,229 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Engine/Level.h"
|
||||
#include "Engine/LevelScriptBlueprint.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "K2Node_CallFunction.h"
|
||||
#include "K2Node_Event.h"
|
||||
#include "K2Node_CustomEvent.h"
|
||||
#include "K2Node_VariableGet.h"
|
||||
#include "K2Node_VariableSet.h"
|
||||
#include "K2Node_BreakStruct.h"
|
||||
#include "K2Node_MakeStruct.h"
|
||||
#include "K2Node_FunctionEntry.h"
|
||||
#include "K2Node_EditablePinBase.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
#include "UMCPHandler_BlueprintSearchTypeUsage.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// ============================================================
|
||||
// HandleSearchByType — find all usages of a type across blueprints
|
||||
// ============================================================
|
||||
|
||||
UCLASS(meta=(Group="Blueprint"))
|
||||
class UMCPHandler_BlueprintSearchTypeUsage : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Type name to search for (e.g. 'FVector', 'MyStruct'). F/E/U prefix is stripped for matching."))
|
||||
FString TypeName;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Filter to blueprints whose name or path contains this substring"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Maximum number of results to return (default 200, max 500)"))
|
||||
int32 MaxResults = 0;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Search all Blueprints for usages of a specific type in variables, function parameters, struct nodes, and pin connections.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
FString DecodedTypeName = MCPUtils::UrlDecode(TypeName);
|
||||
FString FilterStr = Query.IsEmpty() ? FString() : MCPUtils::UrlDecode(Query);
|
||||
|
||||
int32 EffectiveMaxResults = (MaxResults > 0) ? FMath::Clamp(MaxResults, 1, 500) : 200;
|
||||
|
||||
// Strip F/E/U prefix for comparison
|
||||
FString TypeNameNoPrefix = DecodedTypeName;
|
||||
if (TypeNameNoPrefix.StartsWith(TEXT("F")) || TypeNameNoPrefix.StartsWith(TEXT("E")) || TypeNameNoPrefix.StartsWith(TEXT("U")))
|
||||
{
|
||||
TypeNameNoPrefix = TypeNameNoPrefix.Mid(1);
|
||||
}
|
||||
|
||||
auto MatchesType = [&DecodedTypeName, &TypeNameNoPrefix](const FString& TestType) -> bool
|
||||
{
|
||||
return TestType.Equals(DecodedTypeName, ESearchCase::IgnoreCase) ||
|
||||
TestType.Equals(TypeNameNoPrefix, ESearchCase::IgnoreCase);
|
||||
};
|
||||
|
||||
int32 ResultCount = 0;
|
||||
|
||||
// Helper: get subtype name from a pin type
|
||||
auto GetSubtype = [](const FEdGraphPinType& PinType) -> FString
|
||||
{
|
||||
if (PinType.PinSubCategoryObject.IsValid())
|
||||
return PinType.PinSubCategoryObject->GetName();
|
||||
return FString();
|
||||
};
|
||||
|
||||
// Helper: check if a pin type matches
|
||||
auto PinTypeMatches = [&](const FEdGraphPinType& PinType) -> bool
|
||||
{
|
||||
return MatchesType(GetSubtype(PinType)) || MatchesType(PinType.PinCategory.ToString());
|
||||
};
|
||||
|
||||
// Lambda that searches a single Blueprint for type usages
|
||||
auto SearchOneBlueprint = [&](UBlueprint* BP, bool bIsLevel)
|
||||
{
|
||||
FString BPName = MCPUtils::FormatName(BP);
|
||||
|
||||
// Check variables
|
||||
for (const FBPVariableDescription& Var : BP->NewVariables)
|
||||
{
|
||||
if (ResultCount >= EffectiveMaxResults) return;
|
||||
if (!PinTypeMatches(Var.VarType)) continue;
|
||||
|
||||
Result.Appendf(TEXT("variable %s in %s: %s %s\n"),
|
||||
*MCPUtils::FormatName(Var), *BPName,
|
||||
*Var.VarType.PinCategory.ToString(), *GetSubtype(Var.VarType));
|
||||
ResultCount++;
|
||||
}
|
||||
|
||||
// Check graphs for function/event params, struct nodes, and pin connections
|
||||
for (UEdGraphNode* Node : MCPUtils::AllNodes(BP))
|
||||
{
|
||||
if (ResultCount >= EffectiveMaxResults) return;
|
||||
|
||||
// Check FunctionEntry parameters
|
||||
if (auto* FuncEntry = Cast<UK2Node_FunctionEntry>(Node))
|
||||
{
|
||||
for (const TSharedPtr<FUserPinInfo>& PinInfo : FuncEntry->UserDefinedPins)
|
||||
{
|
||||
if (!PinInfo.IsValid()) continue;
|
||||
if (!PinTypeMatches(PinInfo->PinType)) continue;
|
||||
|
||||
Result.Appendf(TEXT("func-param %s.%s in %s: %s %s\n"),
|
||||
*MCPUtils::FormatName(Node->GetGraph()), *PinInfo->PinName.ToString(),
|
||||
*BPName,
|
||||
*PinInfo->PinType.PinCategory.ToString(), *GetSubtype(PinInfo->PinType));
|
||||
ResultCount++;
|
||||
}
|
||||
}
|
||||
else if (auto* CustomEvent = Cast<UK2Node_CustomEvent>(Node))
|
||||
{
|
||||
for (const TSharedPtr<FUserPinInfo>& PinInfo : CustomEvent->UserDefinedPins)
|
||||
{
|
||||
if (!PinInfo.IsValid()) continue;
|
||||
if (!PinTypeMatches(PinInfo->PinType)) continue;
|
||||
|
||||
Result.Appendf(TEXT("event-param %s.%s in %s: %s %s\n"),
|
||||
*MCPUtils::FormatName(Node), *PinInfo->PinName.ToString(),
|
||||
*BPName,
|
||||
*PinInfo->PinType.PinCategory.ToString(), *GetSubtype(PinInfo->PinType));
|
||||
ResultCount++;
|
||||
}
|
||||
}
|
||||
// Check Break/Make struct nodes
|
||||
else if (auto* BreakNode = Cast<UK2Node_BreakStruct>(Node))
|
||||
{
|
||||
if (BreakNode->StructType && MatchesType(BreakNode->StructType->GetName()))
|
||||
{
|
||||
Result.Appendf(TEXT("break-struct %s in %s graph %s\n"),
|
||||
*BreakNode->StructType->GetName(), *BPName,
|
||||
*MCPUtils::FormatName(Node->GetGraph()));
|
||||
ResultCount++;
|
||||
}
|
||||
}
|
||||
else if (auto* MakeNode = Cast<UK2Node_MakeStruct>(Node))
|
||||
{
|
||||
if (MakeNode->StructType && MatchesType(MakeNode->StructType->GetName()))
|
||||
{
|
||||
Result.Appendf(TEXT("make-struct %s in %s graph %s\n"),
|
||||
*MakeNode->StructType->GetName(), *BPName,
|
||||
*MCPUtils::FormatName(Node->GetGraph()));
|
||||
ResultCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check pin connections carrying the type
|
||||
for (UEdGraphPin* Pin : Node->Pins)
|
||||
{
|
||||
if (!Pin || Pin->bHidden || ResultCount >= EffectiveMaxResults) continue;
|
||||
if (Pin->LinkedTo.Num() == 0) continue;
|
||||
if (!PinTypeMatches(Pin->PinType)) continue;
|
||||
|
||||
Result.Appendf(TEXT("pin %s.%s in %s graph %s: %s %s (%d connections)\n"),
|
||||
*MCPUtils::FormatName(Node), *MCPUtils::FormatName(Pin),
|
||||
*BPName, *MCPUtils::FormatName(Node->GetGraph()),
|
||||
*Pin->PinType.PinCategory.ToString(), *GetSubtype(Pin->PinType),
|
||||
Pin->LinkedTo.Num());
|
||||
ResultCount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MCPAssets<UBlueprint> AllBlueprints;
|
||||
AllBlueprints.Info();
|
||||
MCPAssets<UWorld> AllWorlds;
|
||||
AllWorlds.Info();
|
||||
|
||||
// Search regular blueprints
|
||||
for (const FAssetData& Asset : AllBlueprints.AllData())
|
||||
{
|
||||
if (ResultCount >= EffectiveMaxResults) break;
|
||||
|
||||
FString AssetPath = Asset.PackageName.ToString();
|
||||
FString AssetName = Asset.AssetName.ToString();
|
||||
|
||||
if (!FilterStr.IsEmpty() && !AssetName.Contains(FilterStr, ESearchCase::IgnoreCase) &&
|
||||
!AssetPath.Contains(FilterStr, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UBlueprint* BP = Cast<UBlueprint>(const_cast<FAssetData&>(Asset).GetAsset());
|
||||
if (!BP) continue;
|
||||
|
||||
SearchOneBlueprint(BP, false);
|
||||
}
|
||||
|
||||
// Search level blueprints from maps
|
||||
for (const FAssetData& MapAsset : AllWorlds.AllData())
|
||||
{
|
||||
if (ResultCount >= EffectiveMaxResults) break;
|
||||
|
||||
FString AssetPath = MapAsset.PackageName.ToString();
|
||||
FString MapName = MapAsset.AssetName.ToString();
|
||||
|
||||
if (!FilterStr.IsEmpty() && !MapName.Contains(FilterStr, ESearchCase::IgnoreCase) &&
|
||||
!AssetPath.Contains(FilterStr, ESearchCase::IgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UWorld* World = Cast<UWorld>(MapAsset.GetAsset());
|
||||
if (!World || !World->PersistentLevel) continue;
|
||||
ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(false);
|
||||
if (!LevelBP) continue;
|
||||
|
||||
SearchOneBlueprint(LevelBP, true);
|
||||
}
|
||||
|
||||
Result.Appendf(TEXT("\n%d results\n"), ResultCount);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,188 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "MaterialGraph/MaterialGraph.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "UMCPHandler_ConnectMaterialExpressionPins.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
USTRUCT()
|
||||
struct FConnectMaterialPinsEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
FString SourceNode;
|
||||
|
||||
UPROPERTY()
|
||||
FString SourcePin;
|
||||
|
||||
UPROPERTY()
|
||||
FString TargetNode;
|
||||
|
||||
UPROPERTY()
|
||||
FString TargetPin;
|
||||
};
|
||||
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_ConnectMaterialExpressionPins : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)"))
|
||||
FString Material;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)"))
|
||||
FString MaterialFunction;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of {sourceNode, sourcePin, targetNode, targetPin} objects"))
|
||||
FMCPJsonArray Connections;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Connect pins between nodes in a material or material function graph. Supports batching.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
if (Material.IsEmpty() && MaterialFunction.IsEmpty())
|
||||
{
|
||||
Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction'.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Load material or material function
|
||||
UMaterial* MaterialObj = nullptr;
|
||||
UMaterialFunction* MatFunc = nullptr;
|
||||
|
||||
if (!MaterialFunction.IsEmpty())
|
||||
{
|
||||
MCPAssets<UMaterialFunction> Assets;
|
||||
if (!Assets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MatFunc = Assets.Object();
|
||||
}
|
||||
else
|
||||
{
|
||||
MCPAssets<UMaterial> Assets;
|
||||
if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MaterialObj = Assets.Object();
|
||||
}
|
||||
|
||||
if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj);
|
||||
UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr);
|
||||
if (!Graph)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: %s has no material graph.\n"),
|
||||
MaterialObj ? *MCPUtils::FormatName(MaterialObj) : *MCPUtils::FormatName(MatFunc));
|
||||
return;
|
||||
}
|
||||
|
||||
int32 SuccessCount = 0;
|
||||
const UEdGraphSchema* Schema = Graph->GetSchema();
|
||||
|
||||
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
|
||||
{
|
||||
FConnectMaterialPinsEntry Entry;
|
||||
if (!MCPUtils::PopulateFromJson(FConnectMaterialPinsEntry::StaticStruct(), &Entry, ConnVal, MCPErrorCallback(Result)))
|
||||
continue;
|
||||
|
||||
// Find source node
|
||||
UEdGraphNode* SrcNode = FindNodeByName(Graph, Entry.SourceNode);
|
||||
if (!SrcNode)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Source node '%s' not found.\n"), *Entry.SourceNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find target node
|
||||
UEdGraphNode* TgtNode = FindNodeByName(Graph, Entry.TargetNode);
|
||||
if (!TgtNode)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Target node '%s' not found.\n"), *Entry.TargetNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find source pin
|
||||
UEdGraphPin* SrcPin = FindPinByName(SrcNode, Entry.SourcePin);
|
||||
if (!SrcPin)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Pin '%s' not found on %s. Available:"),
|
||||
*Entry.SourcePin, *MCPUtils::FormatName(SrcNode));
|
||||
for (UEdGraphPin* P : SrcNode->Pins)
|
||||
if (P) Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(P));
|
||||
Result.Append(TEXT("\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find target pin
|
||||
UEdGraphPin* TgtPin = FindPinByName(TgtNode, Entry.TargetPin);
|
||||
if (!TgtPin)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Pin '%s' not found on %s. Available:"),
|
||||
*Entry.TargetPin, *MCPUtils::FormatName(TgtNode));
|
||||
for (UEdGraphPin* P : TgtNode->Pins)
|
||||
if (P) Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(P));
|
||||
Result.Append(TEXT("\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check compatibility
|
||||
const FPinConnectionResponse Response = Schema->CanCreateConnection(SrcPin, TgtPin);
|
||||
if (Response.Response == CONNECT_RESPONSE_DISALLOW)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Cannot connect %s.%s -> %s.%s: %s\n"),
|
||||
*MCPUtils::FormatName(SrcNode), *MCPUtils::FormatName(SrcPin),
|
||||
*MCPUtils::FormatName(TgtNode), *MCPUtils::FormatName(TgtPin),
|
||||
*Response.Message.ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
Schema->TryCreateConnection(SrcPin, TgtPin);
|
||||
SuccessCount++;
|
||||
}
|
||||
|
||||
if (SuccessCount > 0)
|
||||
{
|
||||
UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc;
|
||||
Asset->PreEditChange(nullptr);
|
||||
Asset->PostEditChange();
|
||||
bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc);
|
||||
Result.Appendf(TEXT("Connected %d/%d. Saved: %s\n"),
|
||||
SuccessCount, Connections.Array.Num(), bSaved ? TEXT("yes") : TEXT("no"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Appendf(TEXT("0/%d connections succeeded.\n"), Connections.Array.Num());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
UEdGraphNode* FindNodeByName(UEdGraph* Graph, const FString& Name)
|
||||
{
|
||||
for (UEdGraphNode* Node : Graph->Nodes)
|
||||
if (Node && MCPUtils::Identifies(Name, Node))
|
||||
return Node;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UEdGraphPin* FindPinByName(UEdGraphNode* Node, const FString& Name)
|
||||
{
|
||||
for (UEdGraphPin* Pin : Node->Pins)
|
||||
if (Pin && MCPUtils::Identifies(Name, Pin))
|
||||
return Pin;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "MaterialGraph/MaterialGraph.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "UMCPHandler_DeleteMaterialExpression.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_DeleteMaterialExpression : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)"))
|
||||
FString Material;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)"))
|
||||
FString MaterialFunction;
|
||||
|
||||
UPROPERTY(meta=(Description="Expression name (use FormatName from DumpMaterial output)"))
|
||||
FString Node;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="If true, preview the change without applying it"))
|
||||
bool DryRun = false;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Remove an expression node from a material or material function graph.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
if (Material.IsEmpty() && MaterialFunction.IsEmpty())
|
||||
{
|
||||
MCPErrorCallback(Result).SetError(TEXT("Specify 'material' or 'materialFunction'."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Load material or material function
|
||||
UMaterial* MaterialObj = nullptr;
|
||||
UMaterialFunction* MatFunc = nullptr;
|
||||
|
||||
if (!MaterialFunction.IsEmpty())
|
||||
{
|
||||
MCPAssets<UMaterialFunction> MFAssets;
|
||||
if (!MFAssets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MatFunc = MFAssets.Object();
|
||||
}
|
||||
else
|
||||
{
|
||||
MCPAssets<UMaterial> MatAssets;
|
||||
if (!MatAssets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MaterialObj = MatAssets.Object();
|
||||
}
|
||||
|
||||
if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj);
|
||||
UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr);
|
||||
if (!Graph)
|
||||
{
|
||||
MCPErrorCallback(Result).SetError(TEXT("Asset has no material graph."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find node by name
|
||||
UMaterialGraphNode* TargetMatNode = nullptr;
|
||||
for (UEdGraphNode* GraphNode : Graph->Nodes)
|
||||
{
|
||||
if (!GraphNode) continue;
|
||||
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(GraphNode);
|
||||
if (!MatNode || !MatNode->MaterialExpression) continue;
|
||||
if (MCPUtils::Identifies(Node, MatNode->MaterialExpression))
|
||||
{
|
||||
TargetMatNode = MatNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TargetMatNode)
|
||||
{
|
||||
MCPErrorCallback(Result).SetError(FString::Printf(TEXT("Expression '%s' not found."), *Node));
|
||||
return;
|
||||
}
|
||||
|
||||
FString ExprName = MCPUtils::FormatName(TargetMatNode->MaterialExpression);
|
||||
|
||||
if (DryRun)
|
||||
{
|
||||
Result.Appendf(TEXT("DryRun: would delete %s\n"), *ExprName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the expression
|
||||
UMaterialExpression* ExprToRemove = TargetMatNode->MaterialExpression;
|
||||
if (MaterialObj)
|
||||
MaterialObj->GetExpressionCollection().RemoveExpression(ExprToRemove);
|
||||
else
|
||||
MatFunc->GetExpressionCollection().RemoveExpression(ExprToRemove);
|
||||
ExprToRemove->MarkAsGarbage();
|
||||
|
||||
// Rebuild graph
|
||||
Graph->NotifyGraphChanged();
|
||||
|
||||
UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc;
|
||||
Asset->PreEditChange(nullptr);
|
||||
Asset->PostEditChange();
|
||||
Asset->MarkPackageDirty();
|
||||
|
||||
// Save
|
||||
bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc);
|
||||
|
||||
Result.Appendf(TEXT("Deleted %s"), *ExprName);
|
||||
if (!bSaved) Result.Append(TEXT(" (save failed)"));
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,188 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "Materials/MaterialExpressionScalarParameter.h"
|
||||
#include "Materials/MaterialExpressionVectorParameter.h"
|
||||
#include "Materials/MaterialExpressionTextureObjectParameter.h"
|
||||
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
|
||||
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
|
||||
#include "Materials/MaterialExpressionConstant.h"
|
||||
#include "Materials/MaterialExpressionConstant3Vector.h"
|
||||
#include "Materials/MaterialExpressionConstant4Vector.h"
|
||||
#include "Materials/MaterialExpressionTextureSample.h"
|
||||
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "MaterialGraph/MaterialGraph.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "MaterialGraph/MaterialGraphNode_Root.h"
|
||||
#include "MaterialGraph/MaterialGraphSchema.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "UMCPHandler_DescribeMaterialInEnglish.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_DescribeMaterialInEnglish : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Material name or package path"))
|
||||
FString Material;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Generate a human-readable description of a material by tracing its expression graph from the root node inputs.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
MCPAssets<UMaterial> Assets;
|
||||
if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
||||
UMaterial* MaterialObj = Assets.Object();
|
||||
|
||||
// Ensure material graph is built
|
||||
MCPUtils::EnsureMaterialGraph(MaterialObj);
|
||||
if (!MaterialObj->MaterialGraph)
|
||||
{
|
||||
MCPErrorCallback(Result).SetError(TEXT("Could not build MaterialGraph for this material"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find root node
|
||||
UMaterialGraphNode_Root* RootNode = nullptr;
|
||||
for (UEdGraphNode* Node : MaterialObj->MaterialGraph->Nodes)
|
||||
{
|
||||
RootNode = Cast<UMaterialGraphNode_Root>(Node);
|
||||
if (RootNode) break;
|
||||
}
|
||||
if (!RootNode)
|
||||
{
|
||||
MCPErrorCallback(Result).SetError(TEXT("Could not find root node in material graph"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursive helper: trace backwards from a pin and build a description string
|
||||
TFunction<FString(UEdGraphPin*, int32)> TracePin = [&TracePin](UEdGraphPin* Pin, int32 Depth) -> FString
|
||||
{
|
||||
if (!Pin || Depth > 10)
|
||||
return TEXT("(unknown)");
|
||||
|
||||
if (Pin->LinkedTo.Num() == 0)
|
||||
{
|
||||
if (!Pin->DefaultValue.IsEmpty())
|
||||
return FString::Printf(TEXT("(default: %s)"), *Pin->DefaultValue);
|
||||
return TEXT("(unconnected)");
|
||||
}
|
||||
|
||||
TArray<FString> Sources;
|
||||
for (UEdGraphPin* LinkedPin : Pin->LinkedTo)
|
||||
{
|
||||
if (!LinkedPin || !LinkedPin->GetOwningNode()) continue;
|
||||
|
||||
UEdGraphNode* SourceNode = LinkedPin->GetOwningNode();
|
||||
FString NodeDesc;
|
||||
|
||||
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(SourceNode);
|
||||
if (!MatNode)
|
||||
{
|
||||
NodeDesc = MCPUtils::FormatName(SourceNode);
|
||||
Sources.Add(NodeDesc);
|
||||
continue;
|
||||
}
|
||||
|
||||
UMaterialExpression* Expr = MatNode->MaterialExpression;
|
||||
if (!Expr)
|
||||
{
|
||||
NodeDesc = TEXT("(null expression)");
|
||||
}
|
||||
else if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr))
|
||||
{
|
||||
NodeDesc = FString::Printf(TEXT("ScalarParam \"%s\" (default: %.4f)"), *SP->ParameterName.ToString(), SP->DefaultValue);
|
||||
}
|
||||
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
|
||||
{
|
||||
NodeDesc = FString::Printf(TEXT("VectorParam \"%s\" (default: R=%.2f G=%.2f B=%.2f A=%.2f)"),
|
||||
*VP->ParameterName.ToString(), VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A);
|
||||
}
|
||||
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
|
||||
{
|
||||
FString TexName = TP->Texture ? MCPUtils::FormatName(TP->Texture) : TEXT("None");
|
||||
NodeDesc = FString::Printf(TEXT("TextureParam \"%s\" (%s)"), *TP->ParameterName.ToString(), *TexName);
|
||||
}
|
||||
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
|
||||
{
|
||||
NodeDesc = FString::Printf(TEXT("StaticSwitchParam \"%s\" (default: %s)"),
|
||||
*SSP->ParameterName.ToString(), SSP->DefaultValue ? TEXT("true") : TEXT("false"));
|
||||
}
|
||||
else if (auto* SC = Cast<UMaterialExpressionConstant>(Expr))
|
||||
{
|
||||
NodeDesc = FString::Printf(TEXT("Constant(%.4f)"), SC->R);
|
||||
}
|
||||
else if (auto* C3 = Cast<UMaterialExpressionConstant3Vector>(Expr))
|
||||
{
|
||||
NodeDesc = FString::Printf(TEXT("Constant3(R=%.2f G=%.2f B=%.2f)"), C3->Constant.R, C3->Constant.G, C3->Constant.B);
|
||||
}
|
||||
else if (auto* C4 = Cast<UMaterialExpressionConstant4Vector>(Expr))
|
||||
{
|
||||
NodeDesc = FString::Printf(TEXT("Constant4(R=%.2f G=%.2f B=%.2f A=%.2f)"), C4->Constant.R, C4->Constant.G, C4->Constant.B, C4->Constant.A);
|
||||
}
|
||||
else if (auto* TS = Cast<UMaterialExpressionTextureSample>(Expr))
|
||||
{
|
||||
FString TexName = TS->Texture ? MCPUtils::FormatName(TS->Texture) : TEXT("None");
|
||||
NodeDesc = FString::Printf(TEXT("TextureSample(%s)"), *TexName);
|
||||
}
|
||||
else if (auto* MFC = Cast<UMaterialExpressionMaterialFunctionCall>(Expr))
|
||||
{
|
||||
FString FuncName = MFC->MaterialFunction ? MFC->MaterialFunction->GetPathName() : TEXT("None");
|
||||
NodeDesc = FString::Printf(TEXT("FunctionCall(%s)"), *FuncName);
|
||||
}
|
||||
else
|
||||
{
|
||||
NodeDesc = MCPUtils::FormatName(Expr);
|
||||
}
|
||||
|
||||
// Recurse into input pins
|
||||
TArray<FString> InputDescs;
|
||||
for (UEdGraphPin* InputPin : SourceNode->Pins)
|
||||
{
|
||||
if (!InputPin || InputPin->Direction != EGPD_Input || InputPin->LinkedTo.Num() == 0) continue;
|
||||
InputDescs.Add(TracePin(InputPin, Depth + 1));
|
||||
}
|
||||
if (InputDescs.Num() > 0)
|
||||
{
|
||||
NodeDesc += TEXT(" <- (") + FString::Join(InputDescs, TEXT(", ")) + TEXT(")");
|
||||
}
|
||||
|
||||
Sources.Add(NodeDesc);
|
||||
}
|
||||
|
||||
if (Sources.Num() == 1)
|
||||
return Sources[0];
|
||||
|
||||
return TEXT("(") + FString::Join(Sources, TEXT(", ")) + TEXT(")");
|
||||
};
|
||||
|
||||
// Trace each connected root input and output plain text
|
||||
Result.Appendf(TEXT("Material: %s\n\n"), *MCPUtils::FormatName(MaterialObj));
|
||||
|
||||
for (UEdGraphPin* Pin : RootNode->Pins)
|
||||
{
|
||||
if (!Pin || Pin->Direction != EGPD_Input) continue;
|
||||
if (Pin->LinkedTo.Num() == 0) continue;
|
||||
|
||||
FString PinName = MCPUtils::FormatName(Pin);
|
||||
FString Description = TracePin(Pin, 0);
|
||||
Result.Appendf(TEXT("%s <- %s\n"), *PinName, *Description);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "MaterialGraph/MaterialGraph.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "UMCPHandler_DisconnectMaterialExpressionPin.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_DisconnectMaterialExpressionPin : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)"))
|
||||
FString Material;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)"))
|
||||
FString MaterialFunction;
|
||||
|
||||
UPROPERTY(meta=(Description="Node name (use FormatName-style identifier)"))
|
||||
FString Node;
|
||||
|
||||
UPROPERTY(meta=(Description="Pin name to disconnect"))
|
||||
FString PinName;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Break all connections on a specific pin in a material or material function graph.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
if (Material.IsEmpty() && MaterialFunction.IsEmpty())
|
||||
{
|
||||
Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction'.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Load material or material function
|
||||
UMaterial* MaterialObj = nullptr;
|
||||
UMaterialFunction* MatFunc = nullptr;
|
||||
|
||||
if (!MaterialFunction.IsEmpty())
|
||||
{
|
||||
MCPAssets<UMaterialFunction> Assets;
|
||||
if (!Assets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MatFunc = Assets.Object();
|
||||
}
|
||||
else
|
||||
{
|
||||
MCPAssets<UMaterial> Assets;
|
||||
if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MaterialObj = Assets.Object();
|
||||
}
|
||||
|
||||
if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj);
|
||||
UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr);
|
||||
if (!Graph)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: %s has no material graph.\n"),
|
||||
MaterialObj ? *MCPUtils::FormatName(MaterialObj) : *MCPUtils::FormatName(MatFunc));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find node and pin via MCPFetcher
|
||||
MCPFetcher F(Result, Graph);
|
||||
UEdGraphPin* Pin = F.Node(Node).Pin(PinName).Cast<UEdGraphPin>();
|
||||
if (!Pin) return;
|
||||
|
||||
int32 BrokenCount = Pin->LinkedTo.Num();
|
||||
Pin->BreakAllPinLinks();
|
||||
|
||||
UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc;
|
||||
Asset->PreEditChange(nullptr);
|
||||
Asset->PostEditChange();
|
||||
|
||||
bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc);
|
||||
|
||||
Result.Appendf(TEXT("Disconnected %d link(s) from %s on %s.\n"),
|
||||
BrokenCount, *MCPUtils::FormatName(Pin), *MCPUtils::FormatName(Pin->GetOwningNode()));
|
||||
if (!bSaved)
|
||||
Result.Append(TEXT("WARNING: Failed to save package.\n"));
|
||||
}
|
||||
};
|
||||
228
Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterial.h
Normal file
228
Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterial.h
Normal file
@@ -0,0 +1,228 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "MaterialDomain.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "Materials/MaterialExpressionScalarParameter.h"
|
||||
#include "Materials/MaterialExpressionVectorParameter.h"
|
||||
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
|
||||
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
|
||||
#include "Materials/MaterialExpressionTextureSample.h"
|
||||
#include "Engine/Texture.h"
|
||||
#include "UMCPHandler_DumpMaterial.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_DumpMaterial : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Material or MaterialInstance name or package path"))
|
||||
FString Material;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Get detailed info about a material or material instance, including parameters, usage flags, and referenced textures.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
MCPAssets<UMaterialInterface> Assets;
|
||||
Assets.NoScans();
|
||||
Assets.Scan<UMaterial>();
|
||||
Assets.Scan<UMaterialInstanceConstant>();
|
||||
if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
||||
UMaterialInterface* LoadedObj = Assets.Object();
|
||||
|
||||
if (UMaterial* Mat = Cast<UMaterial>(LoadedObj))
|
||||
{
|
||||
EmitMaterial(Mat, Result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (UMaterialInstanceConstant* MI = Cast<UMaterialInstanceConstant>(LoadedObj))
|
||||
{
|
||||
EmitMaterialInstance(MI, Result);
|
||||
return;
|
||||
}
|
||||
|
||||
Result.Appendf(TEXT("ERROR: Loaded object is %s, expected Material or MaterialInstance.\n"),
|
||||
*LoadedObj->GetClass()->GetName());
|
||||
}
|
||||
|
||||
private:
|
||||
void EmitMaterial(UMaterial* Mat, FStringBuilderBase& Result)
|
||||
{
|
||||
Result.Appendf(TEXT("Material: %s\n"), *MCPUtils::FormatName(Mat));
|
||||
Result.Appendf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(Mat->MaterialDomain, TEXT("MD_")));
|
||||
Result.Appendf(TEXT("BlendMode: %s\n"), *MCPUtils::EnumToString(Mat->BlendMode, TEXT("BLEND_")));
|
||||
Result.Appendf(TEXT("TwoSided: %s\n"), Mat->IsTwoSided() ? TEXT("true") : TEXT("false"));
|
||||
|
||||
// Shading models
|
||||
FMaterialShadingModelField SMField = Mat->GetShadingModels();
|
||||
const UEnum* SMEnum = StaticEnum<EMaterialShadingModel>();
|
||||
TArray<FString> SMNames;
|
||||
for (int32 i = 0; i < SMEnum->NumEnums() - 1; ++i)
|
||||
{
|
||||
EMaterialShadingModel SM = (EMaterialShadingModel)SMEnum->GetValueByIndex(i);
|
||||
if (SMField.HasShadingModel(SM))
|
||||
SMNames.Add(SMEnum->GetNameStringByIndex(i));
|
||||
}
|
||||
Result.Appendf(TEXT("ShadingModels: %s\n"), *FString::Join(SMNames, TEXT(", ")));
|
||||
|
||||
// Parameters
|
||||
auto Expressions = Mat->GetExpressions();
|
||||
bool bHasParams = false;
|
||||
for (UMaterialExpression* Expr : Expressions)
|
||||
{
|
||||
if (!Expr) continue;
|
||||
|
||||
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr))
|
||||
{
|
||||
if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; }
|
||||
Result.Appendf(TEXT(" Scalar \"%s\" = %g"), *SP->ParameterName.ToString(), SP->DefaultValue);
|
||||
if (!SP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SP->Group.ToString());
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
|
||||
{
|
||||
if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; }
|
||||
Result.Appendf(TEXT(" Vector \"%s\" = (%.3f, %.3f, %.3f, %.3f)"),
|
||||
*VP->ParameterName.ToString(),
|
||||
VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A);
|
||||
if (!VP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *VP->Group.ToString());
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
|
||||
{
|
||||
if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; }
|
||||
Result.Appendf(TEXT(" Texture \"%s\" = %s"),
|
||||
*TP->ParameterName.ToString(),
|
||||
TP->Texture ? *MCPUtils::FormatName(TP->Texture) : TEXT("None"));
|
||||
if (!TP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *TP->Group.ToString());
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
|
||||
{
|
||||
if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; }
|
||||
Result.Appendf(TEXT(" StaticSwitch \"%s\" = %s"),
|
||||
*SSP->ParameterName.ToString(),
|
||||
SSP->DefaultValue ? TEXT("true") : TEXT("false"));
|
||||
if (!SSP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SSP->Group.ToString());
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Referenced textures
|
||||
auto RefTexObjs = Mat->GetReferencedTextures();
|
||||
bool bHasTextures = false;
|
||||
for (const TObjectPtr<UObject>& TexObj : RefTexObjs)
|
||||
{
|
||||
if (!TexObj) continue;
|
||||
if (!bHasTextures) { Result.Append(TEXT("\nReferenced Textures:\n")); bHasTextures = true; }
|
||||
if (UTexture* Tex = Cast<UTexture>(TexObj.Get()))
|
||||
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Tex));
|
||||
else
|
||||
Result.Appendf(TEXT(" %s\n"), *TexObj->GetPathName());
|
||||
}
|
||||
|
||||
// Usage flags — only print enabled ones
|
||||
Result.Append(TEXT("\nUsage Flags:"));
|
||||
bool bAnyUsage = false;
|
||||
auto EmitFlag = [&](bool bSet, const TCHAR* Name) {
|
||||
if (bSet) { Result.Appendf(TEXT(" %s"), Name); bAnyUsage = true; }
|
||||
};
|
||||
EmitFlag(Mat->bUsedWithSkeletalMesh, TEXT("SkeletalMesh"));
|
||||
EmitFlag(Mat->bUsedWithMorphTargets, TEXT("MorphTargets"));
|
||||
EmitFlag(Mat->bUsedWithNiagaraSprites, TEXT("NiagaraSprites"));
|
||||
EmitFlag(Mat->bUsedWithParticleSprites, TEXT("ParticleSprites"));
|
||||
EmitFlag(Mat->bUsedWithStaticLighting, TEXT("StaticLighting"));
|
||||
if (!bAnyUsage) Result.Append(TEXT(" (none)"));
|
||||
Result.Append(TEXT("\n"));
|
||||
|
||||
// Stats
|
||||
Result.Appendf(TEXT("Expressions: %d\n"), Expressions.Num());
|
||||
int32 TextureSampleCount = 0;
|
||||
for (UMaterialExpression* Expr : Expressions)
|
||||
if (Expr && Expr->IsA<UMaterialExpressionTextureSample>())
|
||||
TextureSampleCount++;
|
||||
Result.Appendf(TEXT("TextureSamples: %d\n"), TextureSampleCount);
|
||||
if (Mat->MaterialGraph)
|
||||
Result.Appendf(TEXT("GraphNodes: %d\n"), Mat->MaterialGraph->Nodes.Num());
|
||||
|
||||
// Additional settings — only print non-default values
|
||||
if (Mat->OpacityMaskClipValue != 0.3333f)
|
||||
Result.Appendf(TEXT("OpacityMaskClipValue: %g\n"), Mat->OpacityMaskClipValue);
|
||||
if (Mat->DitheredLODTransition)
|
||||
Result.Append(TEXT("DitheredLODTransition: true\n"));
|
||||
if (Mat->bAllowNegativeEmissiveColor)
|
||||
Result.Append(TEXT("AllowNegativeEmissiveColor: true\n"));
|
||||
}
|
||||
|
||||
void EmitMaterialInstance(UMaterialInstanceConstant* MI, FStringBuilderBase& Result)
|
||||
{
|
||||
Result.Appendf(TEXT("MaterialInstance: %s\n"), *MCPUtils::FormatName(MI));
|
||||
if (MI->Parent)
|
||||
{
|
||||
if (UMaterial* ParentMat = Cast<UMaterial>(MI->Parent))
|
||||
Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMat));
|
||||
else if (UMaterialInstance* ParentMI = Cast<UMaterialInstance>(MI->Parent))
|
||||
Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMI));
|
||||
else
|
||||
Result.Appendf(TEXT("Parent: %s\n"), *MI->Parent->GetPathName());
|
||||
}
|
||||
|
||||
// Overridden parameters
|
||||
bool bHasParams = false;
|
||||
auto EnsureHeader = [&]() {
|
||||
if (!bHasParams) { Result.Append(TEXT("\nOverridden Parameters:\n")); bHasParams = true; }
|
||||
};
|
||||
|
||||
for (const FScalarParameterValue& P : MI->ScalarParameterValues)
|
||||
{
|
||||
EnsureHeader();
|
||||
Result.Appendf(TEXT(" Scalar \"%s\" = %g\n"), *P.ParameterInfo.Name.ToString(), P.ParameterValue);
|
||||
}
|
||||
|
||||
for (const FVectorParameterValue& P : MI->VectorParameterValues)
|
||||
{
|
||||
EnsureHeader();
|
||||
Result.Appendf(TEXT(" Vector \"%s\" = (%.3f, %.3f, %.3f, %.3f)\n"),
|
||||
*P.ParameterInfo.Name.ToString(),
|
||||
P.ParameterValue.R, P.ParameterValue.G, P.ParameterValue.B, P.ParameterValue.A);
|
||||
}
|
||||
|
||||
for (const FTextureParameterValue& P : MI->TextureParameterValues)
|
||||
{
|
||||
EnsureHeader();
|
||||
if (P.ParameterValue)
|
||||
{
|
||||
Result.Appendf(TEXT(" Texture \"%s\" = %s\n"),
|
||||
*P.ParameterInfo.Name.ToString(), *MCPUtils::FormatName(P.ParameterValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Appendf(TEXT(" Texture \"%s\" = None\n"), *P.ParameterInfo.Name.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
for (const FStaticSwitchParameter& P : MI->GetStaticParameters().StaticSwitchParameters)
|
||||
{
|
||||
EnsureHeader();
|
||||
Result.Appendf(TEXT(" StaticSwitch \"%s\" = %s%s\n"),
|
||||
*P.ParameterInfo.Name.ToString(),
|
||||
P.Value ? TEXT("true") : TEXT("false"),
|
||||
P.bOverride ? TEXT("") : TEXT(" (not overridden)"));
|
||||
}
|
||||
}
|
||||
};
|
||||
147
Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterialFunction.h
Normal file
147
Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterialFunction.h
Normal file
@@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "Materials/MaterialExpressionFunctionInput.h"
|
||||
#include "Materials/MaterialExpressionFunctionOutput.h"
|
||||
#include "Materials/MaterialExpressionMaterialFunctionCall.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/MaterialExpressionTextureObjectParameter.h"
|
||||
#include "Materials/MaterialExpressionTextureSample.h"
|
||||
#include "Materials/MaterialExpressionTextureCoordinate.h"
|
||||
#include "Materials/MaterialExpressionComponentMask.h"
|
||||
#include "Materials/MaterialExpressionCustom.h"
|
||||
#include "Engine/Texture.h"
|
||||
#include "UMCPHandler_DumpMaterialFunction.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_DumpMaterialFunction : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="MaterialFunction name or package path"))
|
||||
FString MaterialFunction;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Get detailed info about a material function, including its inputs, outputs, and expressions.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
MCPAssets<UMaterialFunction> Assets;
|
||||
if (!Assets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return;
|
||||
UMaterialFunction* MF = Assets.Object();
|
||||
|
||||
Result.Appendf(TEXT("MaterialFunction: %s\n"), *MCPUtils::FormatName(MF));
|
||||
|
||||
FString Desc = MF->GetDescription();
|
||||
if (!Desc.IsEmpty())
|
||||
Result.Appendf(TEXT("Description: %s\n"), *Desc);
|
||||
|
||||
auto Expressions = MF->GetExpressions();
|
||||
Result.Appendf(TEXT("Expressions: %d\n"), Expressions.Num());
|
||||
|
||||
// Inputs and outputs
|
||||
bool bHasInputs = false;
|
||||
bool bHasOutputs = false;
|
||||
for (UMaterialExpression* Expr : Expressions)
|
||||
{
|
||||
if (!Expr) continue;
|
||||
if (auto* FI = Cast<UMaterialExpressionFunctionInput>(Expr))
|
||||
{
|
||||
if (!bHasInputs) { Result.Append(TEXT("\nInputs:\n")); bHasInputs = true; }
|
||||
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Expr));
|
||||
}
|
||||
else if (auto* FO = Cast<UMaterialExpressionFunctionOutput>(Expr))
|
||||
{
|
||||
if (!bHasOutputs) { Result.Append(TEXT("\nOutputs:\n")); bHasOutputs = true; }
|
||||
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Expr));
|
||||
}
|
||||
}
|
||||
|
||||
// All expressions
|
||||
Result.Append(TEXT("\nExpression List:\n"));
|
||||
for (UMaterialExpression* Expr : Expressions)
|
||||
{
|
||||
if (!Expr) continue;
|
||||
Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(Expr));
|
||||
EmitExpressionDetails(Expr, Result);
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void EmitExpressionDetails(UMaterialExpression* Expr, FStringBuilderBase& Result)
|
||||
{
|
||||
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" default=%g"), SP->DefaultValue);
|
||||
if (!SP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SP->Group.ToString());
|
||||
}
|
||||
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" default=(%.3f, %.3f, %.3f, %.3f)"),
|
||||
VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A);
|
||||
if (!VP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *VP->Group.ToString());
|
||||
}
|
||||
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" texture=%s"),
|
||||
TP->Texture ? *MCPUtils::FormatName(TP->Texture) : TEXT("None"));
|
||||
if (!TP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *TP->Group.ToString());
|
||||
}
|
||||
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" default=%s"), SSP->DefaultValue ? TEXT("true") : TEXT("false"));
|
||||
if (!SSP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SSP->Group.ToString());
|
||||
}
|
||||
else if (auto* SC = Cast<UMaterialExpressionConstant>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" value=%g"), SC->R);
|
||||
}
|
||||
else if (auto* C3 = Cast<UMaterialExpressionConstant3Vector>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" value=(%.3f, %.3f, %.3f)"), C3->Constant.R, C3->Constant.G, C3->Constant.B);
|
||||
}
|
||||
else if (auto* C4 = Cast<UMaterialExpressionConstant4Vector>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" value=(%.3f, %.3f, %.3f, %.3f)"),
|
||||
C4->Constant.R, C4->Constant.G, C4->Constant.B, C4->Constant.A);
|
||||
}
|
||||
else if (auto* FC = Cast<UMaterialExpressionMaterialFunctionCall>(Expr))
|
||||
{
|
||||
if (FC->MaterialFunction)
|
||||
Result.Appendf(TEXT(" calls=%s"), *FC->MaterialFunction->GetPathName());
|
||||
}
|
||||
else if (auto* TS = Cast<UMaterialExpressionTextureSample>(Expr))
|
||||
{
|
||||
if (TS->Texture)
|
||||
Result.Appendf(TEXT(" texture=%s"), *MCPUtils::FormatName(TS->Texture));
|
||||
}
|
||||
else if (auto* TC = Cast<UMaterialExpressionTextureCoordinate>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" index=%d tiling=(%.1f, %.1f)"), TC->CoordinateIndex, TC->UTiling, TC->VTiling);
|
||||
}
|
||||
else if (auto* Custom = Cast<UMaterialExpressionCustom>(Expr))
|
||||
{
|
||||
Result.Appendf(TEXT(" code_len=%d"), Custom->Code.Len());
|
||||
}
|
||||
}
|
||||
};
|
||||
75
Plugins/UEWingman/Deprecated/UMCPHandler_RemoveStructField.h
Normal file
75
Plugins/UEWingman/Deprecated/UMCPHandler_RemoveStructField.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "StructUtils/UserDefinedStruct.h"
|
||||
#include "UserDefinedStructure/UserDefinedStructEditorData.h"
|
||||
#include "UMCPHandler_RemoveStructField.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_RemoveStructField : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Struct name or package path"))
|
||||
FString Struct;
|
||||
|
||||
UPROPERTY(meta=(Description="Name of the field to remove"))
|
||||
FString FieldName;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Remove a field from a UserDefinedStruct asset.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
MCPFetcher F(Result);
|
||||
UUserDefinedStruct* S = F.Walk(Struct).Cast<UUserDefinedStruct>();
|
||||
if (!S) return;
|
||||
|
||||
// Find the field GUID by name
|
||||
FGuid TargetGuid;
|
||||
bool bFound = false;
|
||||
for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(S))
|
||||
{
|
||||
if (Var.FriendlyName.Equals(FieldName, ESearchCase::IgnoreCase) ||
|
||||
Var.VarName.ToString().Equals(FieldName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
TargetGuid = Var.VarGuid;
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFound)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Field '%s' not found in %s.\nAvailable fields:\n"),
|
||||
*FieldName, *MCPUtils::FormatName(S));
|
||||
for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(S))
|
||||
{
|
||||
Result.Appendf(TEXT(" %s\n"), *Var.FriendlyName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FStructureEditorUtils::RemoveVariable(S, TargetGuid))
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Failed to remove field '%s'."), *FieldName);
|
||||
return;
|
||||
}
|
||||
|
||||
bool bSaved = MCPUtils::SaveGenericPackage(S);
|
||||
Result.Appendf(TEXT("Removed field %s from %s.%s\n"),
|
||||
*FieldName, *MCPUtils::FormatName(S),
|
||||
bSaved ? TEXT("") : TEXT(" WARNING: save failed."));
|
||||
}
|
||||
};
|
||||
106
Plugins/UEWingman/Deprecated/UMCPHandler_SearchWithinMaterials.h
Normal file
106
Plugins/UEWingman/Deprecated/UMCPHandler_SearchWithinMaterials.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "Materials/MaterialExpressionScalarParameter.h"
|
||||
#include "Materials/MaterialExpressionVectorParameter.h"
|
||||
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
|
||||
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
|
||||
#include "UMCPHandler_SearchWithinMaterials.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_SearchWithinMaterials : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Search query string to match against material names, expression classes, and parameter names"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Maximum number of results to return (default 50, max 200)"))
|
||||
int32 MaxResults = 50;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Search across all materials for matching material names, expression types, and parameter names.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
FString DecodedQuery = MCPUtils::UrlDecode(Query);
|
||||
MaxResults = FMath::Clamp(MaxResults, 1, 200);
|
||||
|
||||
int32 Count = 0;
|
||||
|
||||
MCPAssets<UMaterial> AllMaterials;
|
||||
AllMaterials.Load();
|
||||
|
||||
for (UMaterial* MaterialObj : AllMaterials.Objects())
|
||||
{
|
||||
if (Count >= MaxResults) break;
|
||||
if (!MaterialObj) continue;
|
||||
|
||||
FString MatName = MCPUtils::FormatName(MaterialObj);
|
||||
|
||||
// Check material name
|
||||
bool bNameMatch = MatName.Contains(DecodedQuery, ESearchCase::IgnoreCase);
|
||||
|
||||
if (bNameMatch)
|
||||
{
|
||||
Result.Appendf(TEXT("material %s\n"), *MatName);
|
||||
Count++;
|
||||
}
|
||||
|
||||
// Search expressions
|
||||
for (UMaterialExpression* Expr : MaterialObj->GetExpressions())
|
||||
{
|
||||
if (!Expr || Count >= MaxResults) continue;
|
||||
|
||||
FString ExprName = MCPUtils::FormatName(Expr);
|
||||
FString ExprClass = Expr->GetClass()->GetName();
|
||||
FString ExprDesc = Expr->GetDescription();
|
||||
|
||||
// Check parameter name
|
||||
FString ParamName;
|
||||
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr))
|
||||
ParamName = SP->ParameterName.ToString();
|
||||
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
|
||||
ParamName = VP->ParameterName.ToString();
|
||||
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
|
||||
ParamName = TP->ParameterName.ToString();
|
||||
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
|
||||
ParamName = SSP->ParameterName.ToString();
|
||||
|
||||
bool bExprMatch = ExprDesc.Contains(DecodedQuery, ESearchCase::IgnoreCase) ||
|
||||
ExprClass.Contains(DecodedQuery, ESearchCase::IgnoreCase) ||
|
||||
(!ParamName.IsEmpty() && ParamName.Contains(DecodedQuery, ESearchCase::IgnoreCase));
|
||||
|
||||
if (!bExprMatch) continue;
|
||||
|
||||
Result.Appendf(TEXT("expression %s in %s (%s)"), *ExprName, *MatName, *ExprClass);
|
||||
if (!ParamName.IsEmpty())
|
||||
Result.Appendf(TEXT(" param=%s"), *ParamName);
|
||||
Result.Append(TEXT("\n"));
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (Count == 0)
|
||||
{
|
||||
Result.Append(TEXT("No matches found.\n"));
|
||||
}
|
||||
else if (Count >= MaxResults)
|
||||
{
|
||||
Result.Appendf(TEXT("WARNING: Reached limit of %d results. Specify MaxResults to raise it.\n"), MaxResults);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "MaterialGraph/MaterialGraph.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "UMCPHandler_SetMaterialExpressionPosition.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_SetMaterialExpressionPosition : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)"))
|
||||
FString Material;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)"))
|
||||
FString MaterialFunction;
|
||||
|
||||
UPROPERTY(meta=(Description="Expression name (use FormatName from DumpMaterial output)"))
|
||||
FString Node;
|
||||
|
||||
UPROPERTY(meta=(Description="New X position"))
|
||||
int32 PosX = 0;
|
||||
|
||||
UPROPERTY(meta=(Description="New Y position"))
|
||||
int32 PosY = 0;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="If true, preview the change without applying it"))
|
||||
bool DryRun = false;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Reposition a material expression node in the material graph editor.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
if (Material.IsEmpty() && MaterialFunction.IsEmpty())
|
||||
{
|
||||
MCPErrorCallback(Result).SetError(TEXT("Specify 'material' or 'materialFunction'."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Load material or material function
|
||||
UMaterial* MaterialObj = nullptr;
|
||||
UMaterialFunction* MatFunc = nullptr;
|
||||
|
||||
if (!MaterialFunction.IsEmpty())
|
||||
{
|
||||
MCPAssets<UMaterialFunction> MFAssets;
|
||||
if (!MFAssets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MatFunc = MFAssets.Object();
|
||||
}
|
||||
else
|
||||
{
|
||||
MCPAssets<UMaterial> MatAssets;
|
||||
if (!MatAssets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MaterialObj = MatAssets.Object();
|
||||
}
|
||||
|
||||
if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj);
|
||||
UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr);
|
||||
if (!Graph)
|
||||
{
|
||||
MCPErrorCallback(Result).SetError(TEXT("Asset has no material graph."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find node by name
|
||||
UMaterialGraphNode* TargetMatNode = nullptr;
|
||||
for (UEdGraphNode* GraphNode : Graph->Nodes)
|
||||
{
|
||||
if (!GraphNode) continue;
|
||||
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(GraphNode);
|
||||
if (!MatNode || !MatNode->MaterialExpression) continue;
|
||||
if (MCPUtils::Identifies(Node, MatNode->MaterialExpression))
|
||||
{
|
||||
TargetMatNode = MatNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TargetMatNode)
|
||||
{
|
||||
MCPErrorCallback(Result).SetError(FString::Printf(TEXT("Expression '%s' not found."), *Node));
|
||||
return;
|
||||
}
|
||||
|
||||
if (DryRun)
|
||||
{
|
||||
Result.Appendf(TEXT("DryRun: would move %s to (%d, %d)\n"),
|
||||
*MCPUtils::FormatName(TargetMatNode->MaterialExpression), PosX, PosY);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set position on the graph node
|
||||
TargetMatNode->NodePosX = PosX;
|
||||
TargetMatNode->NodePosY = PosY;
|
||||
|
||||
// Also update the underlying expression position
|
||||
if (TargetMatNode->MaterialExpression)
|
||||
{
|
||||
TargetMatNode->MaterialExpression->MaterialExpressionEditorX = PosX;
|
||||
TargetMatNode->MaterialExpression->MaterialExpressionEditorY = PosY;
|
||||
}
|
||||
|
||||
UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc;
|
||||
Asset->PreEditChange(nullptr);
|
||||
Asset->PostEditChange();
|
||||
|
||||
// Save
|
||||
bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc);
|
||||
|
||||
Result.Appendf(TEXT("Moved %s to (%d, %d)"),
|
||||
*MCPUtils::FormatName(TargetMatNode->MaterialExpression), PosX, PosY);
|
||||
if (!bSaved) Result.Append(TEXT(" (save failed)"));
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,282 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPAssets.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "Materials/MaterialExpressionScalarParameter.h"
|
||||
#include "Materials/MaterialExpressionVectorParameter.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 "MaterialGraph/MaterialGraph.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "UMCPHandler_SetMaterialExpressionProperty.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS(meta=(Group="Unclassified"))
|
||||
class UMCPHandler_SetMaterialExpressionProperty : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)"))
|
||||
FString Material;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)"))
|
||||
FString MaterialFunction;
|
||||
|
||||
UPROPERTY(meta=(Description="Expression node name (from FormatName)"))
|
||||
FString Node;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Set the value or properties on a material expression node. "
|
||||
"The 'value' field in the JSON payload provides the new value, whose format depends on the expression type.");
|
||||
}
|
||||
|
||||
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||
{
|
||||
if (Material.IsEmpty() && MaterialFunction.IsEmpty())
|
||||
{
|
||||
Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction'\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Json->HasField(TEXT("value")))
|
||||
{
|
||||
Result.Append(TEXT("ERROR: Missing required field: value\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Load material or material function
|
||||
UMaterial* MaterialObj = nullptr;
|
||||
UMaterialFunction* MatFunc = nullptr;
|
||||
|
||||
if (!MaterialFunction.IsEmpty())
|
||||
{
|
||||
MCPAssets<UMaterialFunction> MFAssets;
|
||||
if (!MFAssets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MatFunc = MFAssets.Object();
|
||||
}
|
||||
else
|
||||
{
|
||||
MCPAssets<UMaterial> MatAssets;
|
||||
if (!MatAssets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
||||
MaterialObj = MatAssets.Object();
|
||||
}
|
||||
|
||||
if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj);
|
||||
UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr);
|
||||
if (!Graph)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: No material graph found\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the node by name using Identifies
|
||||
UMaterialGraphNode* TargetMatNode = nullptr;
|
||||
for (UEdGraphNode* GraphNode : Graph->Nodes)
|
||||
{
|
||||
if (!GraphNode) continue;
|
||||
if (!MCPUtils::Identifies(Node, GraphNode)) continue;
|
||||
TargetMatNode = Cast<UMaterialGraphNode>(GraphNode);
|
||||
if (TargetMatNode) break;
|
||||
}
|
||||
|
||||
if (!TargetMatNode)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Node '%s' not found in material graph\n"), *Node);
|
||||
return;
|
||||
}
|
||||
|
||||
UMaterialExpression* Expr = TargetMatNode->MaterialExpression;
|
||||
if (!Expr)
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: Node '%s' has no material expression\n"), *MCPUtils::FormatName(TargetMatNode));
|
||||
return;
|
||||
}
|
||||
|
||||
UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc;
|
||||
Asset->PreEditChange(nullptr);
|
||||
|
||||
FString SetResult;
|
||||
if (!ApplyValue(Expr, Json, Result, SetResult))
|
||||
{
|
||||
Asset->PostEditChange();
|
||||
return;
|
||||
}
|
||||
|
||||
Asset->PostEditChange();
|
||||
Asset->MarkPackageDirty();
|
||||
|
||||
bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc);
|
||||
|
||||
Result.Appendf(TEXT("%s = %s"), *MCPUtils::FormatName(Expr), *SetResult);
|
||||
if (!bSaved) Result.Append(TEXT(" (save failed)"));
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
|
||||
private:
|
||||
// Apply the value from JSON to the expression. Returns false on error (with message in Result).
|
||||
// On success, fills SetResult with a human-readable summary of the new value.
|
||||
bool ApplyValue(UMaterialExpression* Expr, const FJsonObject* Json, FStringBuilderBase& Result, FString& SetResult)
|
||||
{
|
||||
if (auto* E = Cast<UMaterialExpressionConstant>(Expr))
|
||||
{
|
||||
double Value = Json->GetNumberField(TEXT("value"));
|
||||
E->R = (float)Value;
|
||||
SetResult = FString::Printf(TEXT("%g"), Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* E = Cast<UMaterialExpressionConstant3Vector>(Expr))
|
||||
{
|
||||
FLinearColor C;
|
||||
if (!ParseColorValue(Json, C, false, Result)) return false;
|
||||
E->Constant = C;
|
||||
SetResult = FString::Printf(TEXT("(%.3f, %.3f, %.3f)"), C.R, C.G, C.B);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* E = Cast<UMaterialExpressionConstant4Vector>(Expr))
|
||||
{
|
||||
FLinearColor C;
|
||||
if (!ParseColorValue(Json, C, true, Result)) return false;
|
||||
E->Constant = C;
|
||||
SetResult = FString::Printf(TEXT("(%.3f, %.3f, %.3f, %.3f)"), C.R, C.G, C.B, C.A);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* E = Cast<UMaterialExpressionScalarParameter>(Expr))
|
||||
{
|
||||
double Value = Json->GetNumberField(TEXT("value"));
|
||||
E->DefaultValue = (float)Value;
|
||||
SetResult = FString::Printf(TEXT("%g"), Value);
|
||||
ApplyParameterName(E, Json);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* E = Cast<UMaterialExpressionVectorParameter>(Expr))
|
||||
{
|
||||
FLinearColor C;
|
||||
if (!ParseColorValue(Json, C, true, Result)) return false;
|
||||
E->DefaultValue = C;
|
||||
SetResult = FString::Printf(TEXT("(%.3f, %.3f, %.3f, %.3f)"), C.R, C.G, C.B, C.A);
|
||||
ApplyParameterName(E, Json);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* E = Cast<UMaterialExpressionTextureCoordinate>(Expr))
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* ValueObj = nullptr;
|
||||
if (!Json->TryGetObjectField(TEXT("value"), ValueObj) || !ValueObj || !(*ValueObj).IsValid())
|
||||
{
|
||||
Result.Append(TEXT("ERROR: TextureCoordinate requires value as {coordinateIndex, uTiling, vTiling}\n"));
|
||||
return false;
|
||||
}
|
||||
double CoordIndex = 0, UTiling = 1, VTiling = 1;
|
||||
(*ValueObj)->TryGetNumberField(TEXT("coordinateIndex"), CoordIndex);
|
||||
(*ValueObj)->TryGetNumberField(TEXT("uTiling"), UTiling);
|
||||
(*ValueObj)->TryGetNumberField(TEXT("vTiling"), VTiling);
|
||||
E->CoordinateIndex = (int32)CoordIndex;
|
||||
E->UTiling = (float)UTiling;
|
||||
E->VTiling = (float)VTiling;
|
||||
SetResult = FString::Printf(TEXT("index=%d uTiling=%g vTiling=%g"), (int32)CoordIndex, UTiling, VTiling);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* E = Cast<UMaterialExpressionCustom>(Expr))
|
||||
{
|
||||
FString Code;
|
||||
if (Json->TryGetStringField(TEXT("code"), Code))
|
||||
{
|
||||
E->Code = Code;
|
||||
}
|
||||
else if (Json->HasField(TEXT("value")))
|
||||
{
|
||||
FString ValueStr = Json->GetStringField(TEXT("value"));
|
||||
if (!ValueStr.IsEmpty()) E->Code = ValueStr;
|
||||
}
|
||||
SetResult = FString::Printf(TEXT("code: %d chars"), E->Code.Len());
|
||||
|
||||
FString OutputTypeStr;
|
||||
if (Json->TryGetStringField(TEXT("outputType"), OutputTypeStr) && !OutputTypeStr.IsEmpty())
|
||||
{
|
||||
ECustomMaterialOutputType OutType;
|
||||
if (MCPUtils::StringToEnum(OutputTypeStr, OutType, MCPErrorCallback(Result)))
|
||||
E->OutputType = OutType;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* E = Cast<UMaterialExpressionComponentMask>(Expr))
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* ValueObj = nullptr;
|
||||
if (!Json->TryGetObjectField(TEXT("value"), ValueObj) || !ValueObj || !(*ValueObj).IsValid())
|
||||
{
|
||||
Result.Append(TEXT("ERROR: ComponentMask requires value as {r, g, b, a} (booleans)\n"));
|
||||
return false;
|
||||
}
|
||||
bool bR = false, bG = false, bB = false, bA = false;
|
||||
(*ValueObj)->TryGetBoolField(TEXT("r"), bR);
|
||||
(*ValueObj)->TryGetBoolField(TEXT("g"), bG);
|
||||
(*ValueObj)->TryGetBoolField(TEXT("b"), bB);
|
||||
(*ValueObj)->TryGetBoolField(TEXT("a"), bA);
|
||||
E->R = bR ? 1 : 0;
|
||||
E->G = bG ? 1 : 0;
|
||||
E->B = bB ? 1 : 0;
|
||||
E->A = bA ? 1 : 0;
|
||||
SetResult = FString::Printf(TEXT("R=%s G=%s B=%s A=%s"),
|
||||
bR ? TEXT("true") : TEXT("false"),
|
||||
bG ? TEXT("true") : TEXT("false"),
|
||||
bB ? TEXT("true") : TEXT("false"),
|
||||
bA ? TEXT("true") : TEXT("false"));
|
||||
return true;
|
||||
}
|
||||
|
||||
Result.Appendf(TEXT("ERROR: Expression type '%s' does not support value setting. Supported: "
|
||||
"Constant, Constant3Vector, Constant4Vector, ScalarParameter, VectorParameter, "
|
||||
"TextureCoordinate, Custom, ComponentMask\n"),
|
||||
*Expr->GetClass()->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse {r, g, b[, a]} from the "value" JSON field.
|
||||
bool ParseColorValue(const FJsonObject* Json, FLinearColor& OutColor, bool bHasAlpha, FStringBuilderBase& Result)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* ValueObj = nullptr;
|
||||
if (!Json->TryGetObjectField(TEXT("value"), ValueObj) || !ValueObj || !(*ValueObj).IsValid())
|
||||
{
|
||||
Result.Appendf(TEXT("ERROR: requires value as {r, g, b%s}\n"), bHasAlpha ? TEXT(", a") : TEXT(""));
|
||||
return false;
|
||||
}
|
||||
double R = 0, G = 0, B = 0, A = 1;
|
||||
(*ValueObj)->TryGetNumberField(TEXT("r"), R);
|
||||
(*ValueObj)->TryGetNumberField(TEXT("g"), G);
|
||||
(*ValueObj)->TryGetNumberField(TEXT("b"), B);
|
||||
if (bHasAlpha) (*ValueObj)->TryGetNumberField(TEXT("a"), A);
|
||||
OutColor = FLinearColor((float)R, (float)G, (float)B, (float)A);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the JSON has a "parameterName" field, apply it to a parameter expression.
|
||||
void ApplyParameterName(UMaterialExpressionParameter* Param, const FJsonObject* Json)
|
||||
{
|
||||
FString ParamName;
|
||||
if (Json->TryGetStringField(TEXT("parameterName"), ParamName) && !ParamName.IsEmpty())
|
||||
Param->ParameterName = FName(*ParamName);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user