Lots of useful work
This commit is contained in:
BIN
Content/Testing/M_Test.uasset
LFS
BIN
Content/Testing/M_Test.uasset
LFS
Binary file not shown.
@@ -5,6 +5,7 @@
|
||||
#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"
|
||||
@@ -26,12 +27,6 @@ public:
|
||||
UPROPERTY(meta=(Optional, Description="Filter by parent class name (exact match, case-insensitive)"))
|
||||
FString ParentClass;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Include regular blueprints (default true)"))
|
||||
bool IncludeRegular = true;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Include level blueprints (default true)"))
|
||||
bool IncludeLevel = true;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("List all Blueprint assets in the project, with optional filtering by name, parent class, or type.");
|
||||
@@ -40,30 +35,28 @@ public:
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPAssets<UObject> Assets;
|
||||
Assets.NoScans().Substring(Query).Limit(500);
|
||||
if (IncludeRegular) Assets.Scan<UBlueprint>();
|
||||
if (IncludeLevel) Assets.Scan<UWorld>();
|
||||
Assets.Info();
|
||||
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;
|
||||
if (Asset.AssetClassPath == UWorld::StaticClass()->GetClassPathName())
|
||||
Asset.GetTagValue(FName(TEXT("ParentClass")), ParentClassName);
|
||||
int32 DotIndex;
|
||||
if (ParentClassName.FindLastChar('.', DotIndex))
|
||||
{
|
||||
ParentClassName = TEXT("LevelScriptActor");
|
||||
}
|
||||
else
|
||||
{
|
||||
Asset.GetTagValue(FName(TEXT("ParentClass")), ParentClassName);
|
||||
int32 DotIndex;
|
||||
if (ParentClassName.FindLastChar('.', DotIndex))
|
||||
{
|
||||
ParentClassName = ParentClassName.Mid(DotIndex + 1);
|
||||
}
|
||||
ParentClassName.RemoveFromEnd(TEXT("'"));
|
||||
ParentClassName = ParentClassName.Mid(DotIndex + 1);
|
||||
}
|
||||
ParentClassName.RemoveFromEnd(TEXT("'"));
|
||||
|
||||
// Apply parent class filter
|
||||
if (!ParentClass.IsEmpty())
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPServer.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "MaterialDomain.h"
|
||||
#include "Factories/MaterialFactoryNew.h"
|
||||
#include "MCPPackageMaker.h"
|
||||
#include "Material_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UMCP_Material_Create : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new material (e.g. '/Game/Materials/M_Gold')"))
|
||||
FString AssetPath;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Material domain: Surface, DeferredDecal, LightFunction, Volume, PostProcess, UI"))
|
||||
FString Domain;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Blend mode: Opaque, Masked, Translucent, Additive, Modulate"))
|
||||
FString BlendMode;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Whether the material is two-sided"))
|
||||
bool TwoSided = false;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Create a new UMaterial asset with optional domain, blend mode, and two-sided settings.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPPackageMaker Maker(AssetPath);
|
||||
if (!Maker.Ok()) return;
|
||||
|
||||
// Parse optional enum properties before creating the asset.
|
||||
EMaterialDomain ParsedDomain = MD_Surface;
|
||||
if (!Domain.IsEmpty())
|
||||
{
|
||||
if (!MCPUtils::StringToEnum(Domain, ParsedDomain, TEXT("MD_")))
|
||||
return;
|
||||
}
|
||||
|
||||
EBlendMode ParsedBlendMode = BLEND_Opaque;
|
||||
if (!BlendMode.IsEmpty())
|
||||
{
|
||||
if (!MCPUtils::StringToEnum(BlendMode, ParsedBlendMode, TEXT("BLEND_")))
|
||||
return;
|
||||
}
|
||||
|
||||
// Create via IAssetTools + factory.
|
||||
UMaterial* MaterialObj = Maker.CreateAsset<UMaterial, UMaterialFactoryNew>();
|
||||
if (!MaterialObj) return;
|
||||
|
||||
// Apply optional properties.
|
||||
if (!Domain.IsEmpty())
|
||||
MaterialObj->MaterialDomain = ParsedDomain;
|
||||
|
||||
if (!BlendMode.IsEmpty())
|
||||
MaterialObj->BlendMode = ParsedBlendMode;
|
||||
|
||||
MaterialObj->TwoSided = TwoSided ? 1 : 0;
|
||||
|
||||
bool bSaved = MCPUtils::SaveGenericPackage(MaterialObj);
|
||||
|
||||
UMCPServer::Printf(TEXT("Created %s\n"), *MaterialObj->GetPathName());
|
||||
UMCPServer::Printf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_")));
|
||||
UMCPServer::Printf(TEXT("BlendMode: %s\n"), *MCPUtils::EnumToString(MaterialObj->BlendMode, TEXT("BLEND_")));
|
||||
UMCPServer::Printf(TEXT("TwoSided: %s\n"), MaterialObj->TwoSided ? TEXT("true") : TEXT("false"));
|
||||
if (!bSaved)
|
||||
UMCPServer::Print(TEXT("WARNING: Package save failed\n"));
|
||||
}
|
||||
};
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).ToBlueprint().Cast<UBlueprint>();
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
// Check for duplicate variable name
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).ToBlueprint().Cast<UBlueprint>();
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
FBPVarEditor Editor(BP, Variable);
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).ToBlueprint().Cast<UBlueprint>();
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
FBPVarEditor Editor(BP, Variable);
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).ToBlueprint().Cast<UBlueprint>();
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
FBPVarEditor Editor(BP, Variable);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "MCPToolMenu.h"
|
||||
#include "MCPServer.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "GraphNode_ShowMenu.generated.h"
|
||||
|
||||
|
||||
@@ -34,6 +35,11 @@ private:
|
||||
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
|
||||
if (!NodeObj) return;
|
||||
|
||||
if (Cast<UMaterialGraphNode>(NodeObj))
|
||||
{
|
||||
UMCPServer::Printf(TEXT("Material graph nodes do not have usable context menus."));
|
||||
return;
|
||||
}
|
||||
FToolMenuContext Context;
|
||||
TArray<FToolMenuEntry> Entries = MCPToolMenu::GetMenuItems(NodeObj, Context);
|
||||
for (FToolMenuEntry &Entry : Entries)
|
||||
@@ -41,5 +47,6 @@ private:
|
||||
FString LabelText = Entry.Label.Get().ToString();
|
||||
UMCPServer::Printf(TEXT("%s\n"), *LabelText);
|
||||
}
|
||||
if (Entries.IsEmpty()) UMCPServer::Printf(TEXT("No selectable menu entries right now.\n"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPFetcher F;
|
||||
UEdGraph* G = F.Walk(Graph).ToGraph().Cast<UEdGraph>();
|
||||
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!G) return;
|
||||
|
||||
int32 SuccessCount = 0;
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPFetcher F;
|
||||
UEdGraph* G = F.Walk(Graph).ToGraph().Cast<UEdGraph>();
|
||||
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!G) return;
|
||||
|
||||
int32 SuccessCount = 0;
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPFetcher F;
|
||||
UEdGraph *G = F.Walk(Graph).ToGraph().Cast<UEdGraph>();
|
||||
UEdGraph *G = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!G) return;
|
||||
|
||||
MCPGraphExport Exporter(G);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "MaterialParameter.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "MaterialTypes.h"
|
||||
#include "MaterialInstance_ClearParameter.generated.h"
|
||||
@@ -45,7 +46,7 @@ public:
|
||||
|
||||
// Parse the association string.
|
||||
EMaterialParameterAssociation Association;
|
||||
if (!MCPUtils::ParseMaterialParameterAssociation(ParameterAssociation, Association))
|
||||
if (!MCPMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association))
|
||||
return;
|
||||
|
||||
FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "MaterialParameter.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "MaterialTypes.h"
|
||||
#include "MaterialInstance_DumpParameters.generated.h"
|
||||
@@ -34,7 +35,7 @@ public:
|
||||
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
|
||||
if (!MI) return;
|
||||
|
||||
auto AllParams = MCPUtils::GetMaterialParameters(MI);
|
||||
auto AllParams = MCPMaterialParameter::GetMaterialParameters(MI);
|
||||
|
||||
// Overridden parameters first.
|
||||
bool bHasOverrides = false;
|
||||
@@ -42,7 +43,7 @@ public:
|
||||
{
|
||||
if (!Meta.bOverride) continue;
|
||||
if (!bHasOverrides) { UMCPServer::Print(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; }
|
||||
MCPUtils::FormatMaterialParameter(Info, Meta);
|
||||
MCPMaterialParameter::FormatMaterialParameter(Info, Meta);
|
||||
}
|
||||
|
||||
// Inherited (non-overridden) parameters.
|
||||
@@ -51,7 +52,7 @@ public:
|
||||
{
|
||||
if (Meta.bOverride) continue;
|
||||
if (!bHasInherited) { UMCPServer::Print(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; }
|
||||
MCPUtils::FormatMaterialParameter(Info, Meta);
|
||||
MCPMaterialParameter::FormatMaterialParameter(Info, Meta);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "MaterialParameter.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "MaterialTypes.h"
|
||||
#include "Misc/DefaultValueHelper.h"
|
||||
@@ -49,14 +50,14 @@ public:
|
||||
|
||||
// Parse the association string.
|
||||
EMaterialParameterAssociation Association;
|
||||
if (!MCPUtils::ParseMaterialParameterAssociation(ParameterAssociation, Association))
|
||||
if (!MCPMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association))
|
||||
return;
|
||||
|
||||
// Build the parameter ID to look up.
|
||||
FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);
|
||||
|
||||
// Find it in the material's parameter map.
|
||||
auto AllParams = MCPUtils::GetMaterialParameters(MI);
|
||||
auto AllParams = MCPMaterialParameter::GetMaterialParameters(MI);
|
||||
FMaterialParameterMetadata* Found = AllParams.Find(ParamID);
|
||||
if (!Found)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPServer.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "MaterialDomain.h"
|
||||
#include "Factories/MaterialFactoryNew.h"
|
||||
#include "MCPPackageMaker.h"
|
||||
#include "Material_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UMCP_Material_Create : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new material"))
|
||||
FString Material;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Create a new UMaterial asset");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
MCPPackageMaker Maker(Material);
|
||||
if (!Maker.Ok()) return;
|
||||
|
||||
// Create via IAssetTools + factory.
|
||||
UMaterial* MaterialObj = Maker.CreateAsset<UMaterial, UMaterialFactoryNew>();
|
||||
if (!MaterialObj) return;
|
||||
|
||||
bool bSaved = MCPUtils::SaveGenericPackage(MaterialObj);
|
||||
UMCPServer::Printf(TEXT("Created %s\n"), *MaterialObj->GetPathName());
|
||||
if (!bSaved) UMCPServer::Print(TEXT("WARNING: Package save failed\n"));
|
||||
}
|
||||
};
|
||||
@@ -3,8 +3,9 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPServer.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "MaterialParameter.h"
|
||||
#include "MaterialTypes.h"
|
||||
#include "Material_DumpParameters.generated.h"
|
||||
|
||||
@@ -33,11 +34,12 @@ public:
|
||||
UMaterial* Mat = F.Asset(Material).Cast<UMaterial>();
|
||||
if (!Mat) return;
|
||||
|
||||
auto AllParams = MCPUtils::GetMaterialParameters(Mat);
|
||||
auto AllParams = MCPMaterialParameter::GetMaterialParameters(Mat);
|
||||
|
||||
for (auto& [Info, Meta] : AllParams)
|
||||
{
|
||||
MCPUtils::FormatMaterialParameter(Info, Meta);
|
||||
MCPMaterialParameter::FormatMaterialParameter(Info, Meta);
|
||||
}
|
||||
if (AllParams.IsEmpty()) UMCPServer::Printf(TEXT("No material parameters.\n"));
|
||||
}
|
||||
};
|
||||
@@ -78,8 +78,8 @@ public:
|
||||
{
|
||||
UMCPServer::Printf(TEXT("\n"));
|
||||
EmitCommandList(false);
|
||||
UMCPServer::Print(TEXT("\n--- Half-Baked (may have issues) ---\n\n"));
|
||||
EmitCommandList(true);
|
||||
// UMCPServer::Print(TEXT("\n--- Half-Baked (may have issues) ---\n\n"));
|
||||
// EmitCommandList(true);
|
||||
UMCPServer::Printf(TEXT("\n"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,12 +35,10 @@ public:
|
||||
"\n component — move from a blueprint to a component"
|
||||
"\n levelblueprint — move from a world to a blueprint"
|
||||
"\n"
|
||||
"\n DUMP COMMANDS:"
|
||||
"\n Steps do not always require a parameter. For example, materials"
|
||||
"\n only have one graph, so you can just say:"
|
||||
"\n"
|
||||
"\n There are several commands whose names end in 'Dump'. These"
|
||||
"\n are essential tools for viewing the state of the world."
|
||||
"\n They are particularly important because they show you what"
|
||||
"\n unique IDs exist that you can refer to."
|
||||
"\n /Game/Materials/MyMaterial,graph"
|
||||
"\n"
|
||||
"\n TYPES:"
|
||||
"\n"
|
||||
@@ -80,6 +78,12 @@ public:
|
||||
"\n properties, which actually come from the material expressions."
|
||||
"\n You can edit these using Property_Set on the node."
|
||||
"\n"
|
||||
"\n COMMANDS YOU SHOULD KNOW ABOUT AND REMEMBER:"
|
||||
"\n"
|
||||
"\n UserManual: this explanation"
|
||||
"\n ShowCommands: a full list of all the commands"
|
||||
"\n Graph_Dump: a detailed listing of any UEdGraph"
|
||||
"\n Property_Dump: show information on many objects"
|
||||
"\n"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ MCPFetcher::WalkFunc MCPFetcher::GetWalker(const FString& Step)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void MCPFetcher::SetObj(UObject* InObj)
|
||||
{
|
||||
UMCPServer::AddTouchedObject(InObj);
|
||||
@@ -49,15 +48,26 @@ MCPFetcher& MCPFetcher::SetError()
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MCPFetcher::PathFailed(const TCHAR* Expected)
|
||||
{
|
||||
SetError();
|
||||
if (ResultPin)
|
||||
UMCPServer::Printf(TEXT("ERROR: Path specifies a pin, but expected %s\n"), Expected);
|
||||
else if (Obj)
|
||||
UMCPServer::Printf(TEXT("ERROR: Path specifies a %s, but expected %s\n"), *Obj->GetClass()->GetName(), Expected);
|
||||
else
|
||||
UMCPServer::Printf(TEXT("ERROR: Path led to a null pointer\n"));
|
||||
}
|
||||
|
||||
MCPFetcher& MCPFetcher::TypeMismatch(const TCHAR* Walker, const TCHAR* Expected)
|
||||
{
|
||||
bError = true;
|
||||
SetError();
|
||||
if (ResultPin)
|
||||
UMCPServer::Printf(TEXT("ERROR: Input to '%s' is a pin, expected %s\n"), Walker, Expected);
|
||||
UMCPServer::Printf(TEXT("ERROR: Input to '%s' is a pin, but expected %s\n"), Walker, Expected);
|
||||
else if (Obj)
|
||||
UMCPServer::Printf(TEXT("ERROR: Input to '%s' is %s, expected %s\n"), Walker, *Obj->GetClass()->GetName(), Expected);
|
||||
UMCPServer::Printf(TEXT("ERROR: Input to '%s' is %s, but expected %s\n"), Walker, *Obj->GetClass()->GetName(), Expected);
|
||||
else
|
||||
UMCPServer::Printf(TEXT("ERROR: Input to '%s' is null, expected %s\n"), Walker, Expected);
|
||||
UMCPServer::Printf(TEXT("ERROR: Path led to a null pointer\n"));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -334,47 +344,3 @@ MCPFetcher& MCPFetcher::LevelBlueprint(const FString& Value)
|
||||
return *this;
|
||||
}
|
||||
|
||||
MCPFetcher& MCPFetcher::ToBlueprint()
|
||||
{
|
||||
if (bError) return *this;
|
||||
if (::Cast<UBlueprint>(Obj)) return *this;
|
||||
|
||||
if (UWorld* World = ::Cast<UWorld>(Obj))
|
||||
{
|
||||
if (!World->PersistentLevel)
|
||||
{
|
||||
UMCPServer::Print(TEXT("ERROR: ToBlueprint: World has no PersistentLevel\n"));
|
||||
return SetError();
|
||||
}
|
||||
ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true);
|
||||
if (!LevelBP)
|
||||
{
|
||||
UMCPServer::Print(TEXT("ERROR: ToBlueprint: World has no level blueprint\n"));
|
||||
return SetError();
|
||||
}
|
||||
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)
|
||||
{
|
||||
UMCPServer::Printf(TEXT("ERROR: ToGraph: Material '%s' has no material graph\n"), *Mat->GetName());
|
||||
return SetError();
|
||||
}
|
||||
SetObj(Mat->MaterialGraph);
|
||||
return *this;
|
||||
}
|
||||
|
||||
return TypeMismatch(TEXT("ToGraph"), TEXT("Graph or Material"));
|
||||
}
|
||||
|
||||
@@ -423,9 +423,6 @@ void UMCPServer::ClientThreadFunc(UMCPServer* Server, TSharedPtr<FClientConnecti
|
||||
// Block until the game thread processes this message
|
||||
FString Response = Future.Get();
|
||||
|
||||
// Empty response means either notification or shutdown
|
||||
if (Response.IsEmpty()) continue;
|
||||
|
||||
// Write the response back, null-terminated (blocking)
|
||||
FTCHARToUTF8 Utf8(*Response);
|
||||
int32 BytesSent = 0;
|
||||
|
||||
@@ -514,81 +514,6 @@ UMaterial* MCPUtils::ReplaceMaterialWithTransientCopy(UMaterial* Material)
|
||||
return Material;
|
||||
}
|
||||
|
||||
|
||||
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> MCPUtils::GetMaterialParameters(UMaterialInterface* Material)
|
||||
{
|
||||
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> Result;
|
||||
if (!Material) return Result;
|
||||
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> Temp;
|
||||
for (int32 i = 0; i < (int32)EMaterialParameterType::NumRuntime; i++)
|
||||
{
|
||||
Material->GetAllParametersOfType((EMaterialParameterType)i, Temp);
|
||||
Result.Append(Temp);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool MCPUtils::ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation)
|
||||
{
|
||||
if (Str.Equals(TEXT("Global"), ESearchCase::IgnoreCase))
|
||||
OutAssociation = GlobalParameter;
|
||||
else if (Str.Equals(TEXT("Layer"), ESearchCase::IgnoreCase))
|
||||
OutAssociation = LayerParameter;
|
||||
else if (Str.Equals(TEXT("Blend"), ESearchCase::IgnoreCase))
|
||||
OutAssociation = BlendParameter;
|
||||
else
|
||||
{
|
||||
UMCPServer::Printf(TEXT("ERROR: Invalid ParameterAssociation '%s' (expected 'Global', 'Layer', or 'Blend')\n"), *Str);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MCPUtils::FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta)
|
||||
{
|
||||
// Association prefix for layer/blend parameters.
|
||||
FString Prefix;
|
||||
if (Info.Association == LayerParameter)
|
||||
Prefix = FString::Printf(TEXT("[Layer %d] "), Info.Index);
|
||||
else if (Info.Association == BlendParameter)
|
||||
Prefix = FString::Printf(TEXT("[Blend %d] "), Info.Index);
|
||||
|
||||
switch (Meta.Value.Type)
|
||||
{
|
||||
case EMaterialParameterType::Scalar:
|
||||
UMCPServer::Printf(TEXT(" %sScalar \"%s\" = %g\n"), *Prefix, *Info.Name.ToString(), Meta.Value.AsScalar());
|
||||
break;
|
||||
case EMaterialParameterType::Vector:
|
||||
{
|
||||
FLinearColor C = Meta.Value.AsLinearColor();
|
||||
UMCPServer::Printf(TEXT(" %sVector \"%s\" = (R=%.3f, G=%.3f, B=%.3f, A=%.3f)\n"),
|
||||
*Prefix, *Info.Name.ToString(), C.R, C.G, C.B, C.A);
|
||||
break;
|
||||
}
|
||||
case EMaterialParameterType::DoubleVector:
|
||||
{
|
||||
FVector4d V = Meta.Value.AsVector4d();
|
||||
UMCPServer::Printf(TEXT(" %sDoubleVector \"%s\" = (%.3f, %.3f, %.3f, %.3f)\n"),
|
||||
*Prefix, *Info.Name.ToString(), V.X, V.Y, V.Z, V.W);
|
||||
break;
|
||||
}
|
||||
case EMaterialParameterType::Texture:
|
||||
{
|
||||
UTexture* Tex = Cast<UTexture>(Meta.Value.AsTextureObject());
|
||||
UMCPServer::Printf(TEXT(" %sTexture \"%s\" = %s\n"),
|
||||
*Prefix, *Info.Name.ToString(), Tex ? *MCPUtils::FormatName(Tex) : TEXT("None"));
|
||||
break;
|
||||
}
|
||||
case EMaterialParameterType::StaticSwitch:
|
||||
UMCPServer::Printf(TEXT(" %sStaticSwitch \"%s\" = %s\n"),
|
||||
*Prefix, *Info.Name.ToString(), Meta.Value.AsStaticSwitch() ? TEXT("true") : TEXT("false"));
|
||||
break;
|
||||
default:
|
||||
UMCPServer::Printf(TEXT(" %sType%d \"%s\"\n"), *Prefix, (int)Meta.Value.Type, *Info.Name.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MCPUtils::SaveGenericPackage(UObject* Asset)
|
||||
{
|
||||
if (!Asset) return false;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "MaterialParameter.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "MCPServer.h"
|
||||
|
||||
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> MCPMaterialParameter::GetMaterialParameters(UMaterialInterface* Material)
|
||||
{
|
||||
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> Result;
|
||||
if (!Material) return Result;
|
||||
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> Temp;
|
||||
for (int32 i = 0; i < (int32)EMaterialParameterType::NumRuntime; i++)
|
||||
{
|
||||
Material->GetAllParametersOfType((EMaterialParameterType)i, Temp);
|
||||
Result.Append(Temp);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool MCPMaterialParameter::ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation)
|
||||
{
|
||||
if (Str.Equals(TEXT("Global"), ESearchCase::IgnoreCase))
|
||||
OutAssociation = GlobalParameter;
|
||||
else if (Str.Equals(TEXT("Layer"), ESearchCase::IgnoreCase))
|
||||
OutAssociation = LayerParameter;
|
||||
else if (Str.Equals(TEXT("Blend"), ESearchCase::IgnoreCase))
|
||||
OutAssociation = BlendParameter;
|
||||
else
|
||||
{
|
||||
UMCPServer::Printf(TEXT("ERROR: Invalid ParameterAssociation '%s' (expected 'Global', 'Layer', or 'Blend')\n"), *Str);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MCPMaterialParameter::FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta)
|
||||
{
|
||||
// Association prefix for layer/blend parameters.
|
||||
FString Prefix;
|
||||
if (Info.Association == LayerParameter)
|
||||
Prefix = FString::Printf(TEXT("[Layer %d] "), Info.Index);
|
||||
else if (Info.Association == BlendParameter)
|
||||
Prefix = FString::Printf(TEXT("[Blend %d] "), Info.Index);
|
||||
|
||||
switch (Meta.Value.Type)
|
||||
{
|
||||
case EMaterialParameterType::Scalar:
|
||||
UMCPServer::Printf(TEXT(" %sScalar \"%s\" = %g\n"), *Prefix, *Info.Name.ToString(), Meta.Value.AsScalar());
|
||||
break;
|
||||
case EMaterialParameterType::Vector:
|
||||
{
|
||||
FLinearColor C = Meta.Value.AsLinearColor();
|
||||
UMCPServer::Printf(TEXT(" %sVector \"%s\" = (R=%.3f, G=%.3f, B=%.3f, A=%.3f)\n"),
|
||||
*Prefix, *Info.Name.ToString(), C.R, C.G, C.B, C.A);
|
||||
break;
|
||||
}
|
||||
case EMaterialParameterType::DoubleVector:
|
||||
{
|
||||
FVector4d V = Meta.Value.AsVector4d();
|
||||
UMCPServer::Printf(TEXT(" %sDoubleVector \"%s\" = (%.3f, %.3f, %.3f, %.3f)\n"),
|
||||
*Prefix, *Info.Name.ToString(), V.X, V.Y, V.Z, V.W);
|
||||
break;
|
||||
}
|
||||
case EMaterialParameterType::Texture:
|
||||
{
|
||||
UTexture* Tex = Cast<UTexture>(Meta.Value.AsTextureObject());
|
||||
UMCPServer::Printf(TEXT(" %sTexture \"%s\" = %s\n"),
|
||||
*Prefix, *Info.Name.ToString(), Tex ? *MCPUtils::FormatName(Tex) : TEXT("None"));
|
||||
break;
|
||||
}
|
||||
case EMaterialParameterType::StaticSwitch:
|
||||
UMCPServer::Printf(TEXT(" %sStaticSwitch \"%s\" = %s\n"),
|
||||
*Prefix, *Info.Name.ToString(), Meta.Value.AsStaticSwitch() ? TEXT("true") : TEXT("false"));
|
||||
break;
|
||||
default:
|
||||
UMCPServer::Printf(TEXT(" %sType%d \"%s\"\n"), *Prefix, (int)Meta.Value.Type, *Info.Name.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,15 +54,6 @@ public:
|
||||
MCPFetcher& Component(const FString& Value);
|
||||
MCPFetcher& LevelBlueprint(const FString& Value);
|
||||
|
||||
// The following walkers cannot be invoked from
|
||||
// paths, only procedurally. If the current object
|
||||
// is already the target type, they do nothing.
|
||||
// Otherwise, they attempt to convert (e.g. World
|
||||
// to its level blueprint, Material to its graph).
|
||||
//
|
||||
MCPFetcher& ToBlueprint();
|
||||
MCPFetcher& ToGraph();
|
||||
|
||||
// Return true if there haven't been any errors.
|
||||
// Note that errors always automatically generate
|
||||
// output to MCPServer::Printf.
|
||||
@@ -77,8 +68,7 @@ public:
|
||||
{
|
||||
if (bError) return nullptr;
|
||||
T* Result = ::Cast<T>(Obj);
|
||||
if (Result == nullptr)
|
||||
TypeMismatch(TEXT("Cast"), *T::StaticClass()->GetName());
|
||||
if (Result == nullptr) PathFailed(*T::StaticClass()->GetName());
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -144,6 +134,7 @@ private:
|
||||
void SetObj(UObject* InObj);
|
||||
void SetPin(UEdGraphPin* InPin);
|
||||
MCPFetcher& SetError();
|
||||
void PathFailed(const TCHAR *Kind);
|
||||
MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected);
|
||||
bool CheckAssetIsA(UClass* StaticClass);
|
||||
WalkFunc GetWalker(const FString &Step);
|
||||
@@ -152,7 +143,6 @@ private:
|
||||
template<> inline UEdGraphPin* MCPFetcher::Cast<UEdGraphPin>()
|
||||
{
|
||||
if (bError) return nullptr;
|
||||
if (!ResultPin)
|
||||
TypeMismatch(TEXT("Cast"), TEXT("pin"));
|
||||
if (!ResultPin) PathFailed(TEXT("UEdGraphPin"));
|
||||
return ResultPin;
|
||||
}
|
||||
|
||||
@@ -153,11 +153,6 @@ public:
|
||||
static FString ActionFullName(const TSharedPtr<FEdGraphSchemaAction>& Action);
|
||||
static TArray<TSharedPtr<FEdGraphSchemaAction>> SearchGraphActions(UEdGraph* Graph, const FString& Query, int32 MaxResults = 0, bool ExactMatch = false);
|
||||
|
||||
// ----- Material Parameters -----
|
||||
static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material);
|
||||
static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation);
|
||||
static void FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
|
||||
|
||||
// ----- Editable template -----
|
||||
static TArray<FProperty*> SearchProperties(UObject* Obj, const FString& Query, EPropertyFlags Flags, bool bLocal);
|
||||
static FProperty* FindPropertyByName(UObject* Obj, const FString& Name);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Materials/Material.h"
|
||||
|
||||
class MCPMaterialParameter
|
||||
{
|
||||
public:
|
||||
static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material);
|
||||
static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation);
|
||||
static void FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
|
||||
};
|
||||
|
||||
@@ -20,7 +20,6 @@ TOOL_DESCRIPTION = (
|
||||
"Send a command to the Unreal Editor's BlueprintMCP plugin. "
|
||||
"The 'command' field specifies which operation to perform; "
|
||||
"additional fields are command-specific parameters. "
|
||||
'Use {"command": "ShowCommands"} to list available commands. '
|
||||
'Use {"command": "UserManual"} to get an overview. '
|
||||
"If the editor is not running, the call will return an error; "
|
||||
"just ask the user to start the editor and try again."
|
||||
|
||||
Reference in New Issue
Block a user