Working on editing MG
This commit is contained in:
@@ -34,22 +34,22 @@ class UMCP_GraphPin_Connect : public UObject, public IMCPHandler
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Blueprint name or package path"))
|
||||
FString Blueprint;
|
||||
UPROPERTY(meta=(Description="Path to a graph, e.g. /Game/Foo,graph:EventGraph or /Game/Materials/M_Test"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of {sourcePin, targetPin} objects. Each pin is a path like node:MyNode,pin:Output"))
|
||||
FMCPJsonArray Connections;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Connect pins between nodes in a Blueprint graph.");
|
||||
return TEXT("Connect pins between nodes in a graph (Blueprint or Material).");
|
||||
}
|
||||
|
||||
virtual void Handle(FStringBuilderBase& Result) override
|
||||
{
|
||||
MCPFetcher F(Result);
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
UEdGraph* G = F.Walk(Graph).ToGraph().Cast<UEdGraph>();
|
||||
if (!G) return;
|
||||
|
||||
F.PreEdit();
|
||||
|
||||
@@ -62,15 +62,15 @@ public:
|
||||
if (!MCPUtils::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal, Result))
|
||||
continue;
|
||||
|
||||
MCPFetcher FS(Result, BP);
|
||||
MCPFetcher FS(Result, G);
|
||||
UEdGraphPin* SourcePin = FS.Walk(Entry.SourcePin).Cast<UEdGraphPin>();
|
||||
if (!SourcePin) continue;
|
||||
|
||||
MCPFetcher FT(Result, BP);
|
||||
MCPFetcher FT(Result, G);
|
||||
UEdGraphPin* TargetPin = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>();
|
||||
if (!TargetPin) continue;
|
||||
|
||||
const UEdGraphSchema* Schema = SourcePin->GetOwningNode()->GetGraph()->GetSchema();
|
||||
const UEdGraphSchema* Schema = G->GetSchema();
|
||||
const FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin);
|
||||
if (Response.Response == CONNECT_RESPONSE_DISALLOW)
|
||||
{
|
||||
|
||||
@@ -33,23 +33,23 @@ class UMCP_GraphPin_Disconnect : public UObject, public IMCPHandler
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Blueprint name or package path"))
|
||||
FString Blueprint;
|
||||
UPROPERTY(meta=(Description="Path to a graph, e.g. /Game/Foo,graph:EventGraph or /Game/Materials/M_Test"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of {pin, targetPin?} objects. Each pin is a path like node:MyNode,pin:Output. If targetPin is omitted, all connections on the pin are broken."))
|
||||
FMCPJsonArray Disconnections;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Disconnect pins in a Blueprint graph. "
|
||||
return TEXT("Disconnect pins in a graph (Blueprint or Material). "
|
||||
"Can disconnect a specific link or all links on a pin.");
|
||||
}
|
||||
|
||||
virtual void Handle(FStringBuilderBase& Result) override
|
||||
{
|
||||
MCPFetcher F(Result);
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
UEdGraph* G = F.Walk(Graph).ToGraph().Cast<UEdGraph>();
|
||||
if (!G) return;
|
||||
|
||||
F.PreEdit();
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
FDisconnectPinEntry Entry;
|
||||
if (!MCPUtils::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal, Result)) continue;
|
||||
|
||||
MCPFetcher FP(Result, BP);
|
||||
MCPFetcher FP(Result, G);
|
||||
UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast<UEdGraphPin>();
|
||||
if (!Pin) continue;
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
|
||||
if (!Entry.TargetPin.IsEmpty())
|
||||
{
|
||||
MCPFetcher FT(Result, BP);
|
||||
MCPFetcher FT(Result, G);
|
||||
UEdGraphPin* Target = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>();
|
||||
if (!Target) continue;
|
||||
|
||||
|
||||
@@ -76,23 +76,28 @@ public:
|
||||
continue;
|
||||
}
|
||||
|
||||
const UEdGraphSchema_K2* Schema = Cast<UEdGraphSchema_K2>(Node->GetGraph()->GetSchema());
|
||||
check(Schema);
|
||||
|
||||
// Parse and validate the value before applying.
|
||||
FString UseDefaultValue;
|
||||
TObjectPtr<UObject> UseDefaultObject = nullptr;
|
||||
FText UseDefaultText;
|
||||
Schema->GetPinDefaultValuesFromString(Pin->PinType, Node, Entry.Value, UseDefaultValue, UseDefaultObject, UseDefaultText, false);
|
||||
FString Error = Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
|
||||
if (!Error.IsEmpty())
|
||||
{
|
||||
Result.Appendf(TEXT("error: %s: %s\n"), *MCPUtils::FormatName(Pin), *Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
Pin->Modify();
|
||||
Schema->TrySetDefaultValue(*Pin, Entry.Value);
|
||||
|
||||
// K2 schemas support validation before setting; other schemas (e.g. material) don't.
|
||||
const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(Node->GetGraph()->GetSchema());
|
||||
if (K2Schema)
|
||||
{
|
||||
FString UseDefaultValue;
|
||||
TObjectPtr<UObject> UseDefaultObject = nullptr;
|
||||
FText UseDefaultText;
|
||||
K2Schema->GetPinDefaultValuesFromString(Pin->PinType, Node, Entry.Value, UseDefaultValue, UseDefaultObject, UseDefaultText, false);
|
||||
FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
|
||||
if (!Error.IsEmpty())
|
||||
{
|
||||
Result.Appendf(TEXT("error: %s: %s\n"), *MCPUtils::FormatName(Pin), *Error);
|
||||
continue;
|
||||
}
|
||||
K2Schema->TrySetDefaultValue(*Pin, Entry.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Node->GetGraph()->GetSchema()->TrySetDefaultValue(*Pin, Entry.Value);
|
||||
}
|
||||
|
||||
SuccessCount++;
|
||||
ModifiedNodes.Add(Node);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "K2Node_VariableGet.h"
|
||||
#include "K2Node_CallFunction.h"
|
||||
#include "K2Node_FunctionEntry.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
|
||||
static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix)
|
||||
{
|
||||
@@ -47,7 +48,7 @@ FlxBlueprintExporter::FlxBlueprintExporter(UEdGraph* InGraph)
|
||||
: Graph(InGraph)
|
||||
{
|
||||
SortNodes();
|
||||
EmitLocalVariables();
|
||||
EmitLocalVariables();
|
||||
EmitDetails();
|
||||
EmitGraph();
|
||||
EmitComments();
|
||||
@@ -232,6 +233,9 @@ void FlxBlueprintExporter::EmitNode(UEdGraphNode* Node)
|
||||
|
||||
Output.Appendf(TEXT("\nnode %s: %s\n"), *MCPUtils::FormatName(Node), *MCPUtils::FormatNodeTitle(Node));
|
||||
|
||||
// Emit material expression properties (if applicable).
|
||||
EmitMaterialProperties(Node, Output, true);
|
||||
|
||||
// Emit input data pins.
|
||||
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Input))
|
||||
{
|
||||
@@ -274,6 +278,39 @@ void FlxBlueprintExporter::EmitNode(UEdGraphNode* Node)
|
||||
}
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::EmitMaterialProperty(UMaterialExpression* Expression, FProperty* Prop, FStringBuilderBase& Out)
|
||||
{
|
||||
FString ValueStr = MCPUtils::GetPropertyValueText(Expression, Prop);
|
||||
ValueStr.ReplaceInline(TEXT("\r\n"), TEXT(" "));
|
||||
ValueStr.ReplaceInline(TEXT("\n"), TEXT(" "));
|
||||
if (ValueStr.Len() > 80)
|
||||
ValueStr = ValueStr.Left(80) + TEXT("...");
|
||||
|
||||
bool bEditable = !Prop->HasAnyPropertyFlags(CPF_EditConst);
|
||||
Out.Appendf(TEXT(" %s %s %s = %s\n"),
|
||||
bEditable ? TEXT("editable") : TEXT("readonly"),
|
||||
*MCPUtils::FormatPropertyType(Prop),
|
||||
*MCPUtils::FormatName(Prop),
|
||||
*ValueStr);
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::EmitMaterialProperties(UEdGraphNode* Node, FStringBuilderBase& Out, bool bPrimary)
|
||||
{
|
||||
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node);
|
||||
if (!MatNode || !MatNode->MaterialExpression) return;
|
||||
|
||||
UMaterialExpression* Expression = MatNode->MaterialExpression;
|
||||
FString PrimaryCategory = Expression->GetClass()->GetName();
|
||||
TArray<FProperty*> Props = MCPUtils::SearchProperties(Expression, FString(), CPF_Edit, false);
|
||||
|
||||
for (FProperty* Prop : Props)
|
||||
{
|
||||
FString Category = Prop->HasMetaData(TEXT("Category")) ? Prop->GetMetaData(TEXT("Category")) : FString();
|
||||
if ((Category == PrimaryCategory) == bPrimary)
|
||||
EmitMaterialProperty(Expression, Prop, Out);
|
||||
}
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::EmitLocalVariables()
|
||||
{
|
||||
for (UEdGraphNode* Node : Graph->Nodes)
|
||||
@@ -312,6 +349,8 @@ void FlxBlueprintExporter::EmitDetails()
|
||||
if (Node->IsA<UK2Node_VariableGet>()) continue;
|
||||
Details.Appendf(TEXT("details %s\n"), *MCPUtils::FormatName(Node));
|
||||
Details.Appendf(TEXT(" pos %d, %d\n"), Node->NodePosX, Node->NodePosY);
|
||||
|
||||
EmitMaterialProperties(Node, Details, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +366,7 @@ void FlxBlueprintExporter::EmitComments()
|
||||
int32 CH = CommentNode->NodeHeight;
|
||||
|
||||
// Emit header.
|
||||
Output.Append(TEXT("\ncomment:\n"));
|
||||
Output.Appendf(TEXT("\ncomment %s:\n"), *MCPUtils::FormatName(CommentNode));
|
||||
|
||||
// Emit wrapped, indented body.
|
||||
Output.Append(WrapText(CommentNode->NodeComment, 70, TEXT(" - ")));
|
||||
|
||||
@@ -267,6 +267,42 @@ MCPFetcher& MCPFetcher::Template()
|
||||
return *this;
|
||||
}
|
||||
|
||||
MCPFetcher& MCPFetcher::ToBlueprint()
|
||||
{
|
||||
if (bError) return *this;
|
||||
if (::Cast<UBlueprint>(Obj)) return *this;
|
||||
|
||||
if (UWorld* World = ::Cast<UWorld>(Obj))
|
||||
{
|
||||
if (!World->PersistentLevel)
|
||||
return SetError(TEXT("ToBlueprint: World has no PersistentLevel"));
|
||||
ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true);
|
||||
if (!LevelBP)
|
||||
return SetError(TEXT("ToBlueprint: World has no level blueprint"));
|
||||
SetObj(LevelBP);
|
||||
return *this;
|
||||
}
|
||||
|
||||
return TypeMismatch(TEXT("ToBlueprint"), TEXT("Blueprint or World"));
|
||||
}
|
||||
|
||||
MCPFetcher& MCPFetcher::ToGraph()
|
||||
{
|
||||
if (bError) return *this;
|
||||
if (::Cast<UEdGraph>(Obj)) return *this;
|
||||
|
||||
if (UMaterial* Mat = ::Cast<UMaterial>(Obj))
|
||||
{
|
||||
MCPUtils::EnsureMaterialGraph(Mat);
|
||||
if (!Mat->MaterialGraph)
|
||||
return SetError(FString::Printf(TEXT("ToGraph: Material '%s' has no material graph"), *Mat->GetName()));
|
||||
SetObj(Mat->MaterialGraph);
|
||||
return *this;
|
||||
}
|
||||
|
||||
return TypeMismatch(TEXT("ToGraph"), TEXT("Graph or Material"));
|
||||
}
|
||||
|
||||
MCPFetcher& MCPFetcher::MatExp(const FString& Value)
|
||||
{
|
||||
if (bError) return *this;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
|
||||
class UMaterialExpression;
|
||||
|
||||
class FlxBlueprintExporter
|
||||
{
|
||||
public:
|
||||
@@ -60,6 +62,8 @@ private:
|
||||
void Traverse(UEdGraphNode* Node);
|
||||
void SortNodes();
|
||||
void EmitNode(UEdGraphNode* Node);
|
||||
void EmitMaterialProperty(UMaterialExpression* Expression, FProperty* Prop, FStringBuilderBase& Out);
|
||||
void EmitMaterialProperties(UEdGraphNode* Node, FStringBuilderBase& Out, bool bPrimary);
|
||||
void EmitLocalVariables();
|
||||
void EmitGraph();
|
||||
void EmitDetails();
|
||||
|
||||
@@ -53,6 +53,10 @@ public:
|
||||
// (e.g. Blueprint → CDO, everything else → as-is).
|
||||
MCPFetcher& Template();
|
||||
|
||||
// C++-only navigation: drill down to a specific type.
|
||||
MCPFetcher& ToBlueprint();
|
||||
MCPFetcher& ToGraph();
|
||||
|
||||
// Walker table entry.
|
||||
struct FWalker
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user