Lots of useful work

This commit is contained in:
2026-03-17 17:24:35 -04:00
parent a1131130af
commit cad9947670
27 changed files with 208 additions and 277 deletions

View File

@@ -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())

View File

@@ -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"));
}
};

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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"));
}
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
}
};

View File

@@ -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)
{

View File

@@ -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"));
}
};

View File

@@ -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"));
}
};

View File

@@ -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"));
}
};

View File

@@ -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"
));
}

View File

@@ -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"));
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
};