MI stuff is now workign

This commit is contained in:
2026-03-12 16:54:22 -04:00
parent 73b5d67b96
commit c7fb52a775
9 changed files with 245 additions and 387 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,80 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "Materials/MaterialInstanceConstant.h"
#include "MaterialTypes.h"
#include "MaterialInstance_ClearParameter.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UMCP_MaterialInstance_ClearParameter : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Material Instance path"))
FString Path;
UPROPERTY(meta=(Description="Parameter name to clear"))
FString Parameter;
UPROPERTY(meta=(Description="Parameter association: 'Global', 'Layer', or 'Blend'. Default: 'Global'", Optional))
FString ParameterAssociation = TEXT("Global");
UPROPERTY(meta=(Description="Layer/blend index (0-based). Only used when ParameterAssociation is 'Layer' or 'Blend'", Optional))
int32 ParameterLayer = INDEX_NONE;
virtual FString GetDescription() const override
{
return TEXT("Remove a parameter override from a Material Instance, reverting it to the parent material's value.");
}
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{
MCPFetcher F(Result);
UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>();
if (!MI) return;
// Parse the association string.
EMaterialParameterAssociation Association;
if (!MCPUtils::ParseMaterialParameterAssociation(ParameterAssociation, Association, Result))
return;
FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);
// Remove the override from all parameter arrays.
auto RemoveFrom = [&](auto& Arr) {
return Arr.RemoveAll([&](auto& Entry) { return Entry.ParameterInfo == ParamID; });
};
F.PreEdit();
int32 Removed = 0;
Removed += RemoveFrom(MI->ScalarParameterValues);
Removed += RemoveFrom(MI->VectorParameterValues);
Removed += RemoveFrom(MI->DoubleVectorParameterValues);
Removed += RemoveFrom(MI->TextureParameterValues);
Removed += RemoveFrom(MI->TextureCollectionParameterValues);
Removed += RemoveFrom(MI->RuntimeVirtualTextureParameterValues);
Removed += RemoveFrom(MI->SparseVolumeTextureParameterValues);
Removed += RemoveFrom(MI->FontParameterValues);
F.PostEdit();
if (Removed == 0)
{
Result.Appendf(TEXT("No override found for parameter '%s' (association=%s layer=%d) on %s"),
*Parameter, *ParameterAssociation, ParameterLayer, *MCPUtils::FormatName(MI));
return;
}
MCPUtils::SaveGenericPackage(MI);
Result.Appendf(TEXT("Cleared override for '%s' on %s\n"),
*Parameter, *MCPUtils::FormatName(MI));
}
};

View File

@@ -2,16 +2,10 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h"
#include "Materials/MaterialInterface.h"
#include "Materials/MaterialInstanceConstant.h" #include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialExpressionScalarParameter.h" #include "MaterialTypes.h"
#include "Materials/MaterialExpressionVectorParameter.h"
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
#include "Engine/Texture.h"
#include "MaterialInstance_DumpParameters.generated.h" #include "MaterialInstance_DumpParameters.generated.h"
@@ -25,144 +19,38 @@ class UMCP_MaterialInstance_DumpParameters : public UObject, public IMCPHandler
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY(meta=(Description="Material Instance name or path to inspect")) UPROPERTY(meta=(Description="Material Instance path"))
FString MaterialInstance; FString Path;
virtual FString GetDescription() const override virtual FString GetDescription() const override
{ {
return TEXT("List all parameters on a Material Instance, including overridden and inherited parameters."); return TEXT("List all parameters on a Material Instance, showing current values and which are overridden.");
} }
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{ {
MCPAssets<UMaterialInstanceConstant> Assets; MCPFetcher F(Result);
if (!Assets.Exact(MaterialInstance).Errors(Result).ENone().ETwo().Load()) return; UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>();
UMaterialInstanceConstant* MI = Assets.Object(); if (!MI) return;
Result.Appendf(TEXT("MaterialInstance: %s\n"), *MCPUtils::FormatName(MI)); auto AllParams = MCPUtils::GetMaterialParameters(MI);
// Parent chain // Overridden parameters first.
if (MI->Parent)
{
Result.Append(TEXT("Parent chain:"));
UMaterialInterface* Current = MI->Parent;
while (Current)
{
if (UMaterial* Mat = Cast<UMaterial>(Current))
{
Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(Mat));
break;
}
if (UMaterialInstance* ParentMI = Cast<UMaterialInstance>(Current))
{
Result.Appendf(TEXT(" %s ->"), *MCPUtils::FormatName(ParentMI));
UMaterialInstanceConstant* ParentMIC = Cast<UMaterialInstanceConstant>(Current);
Current = ParentMIC ? ParentMIC->Parent : nullptr;
}
else
{
Result.Appendf(TEXT(" %s"), *Current->GetPathName());
break;
}
}
Result.Append(TEXT("\n"));
}
// Collect names of overridden parameters for filtering inherited ones
TSet<FString> OverriddenScalars, OverriddenVectors, OverriddenTextures, OverriddenStaticSwitches;
for (const FScalarParameterValue& P : MI->ScalarParameterValues)
OverriddenScalars.Add(P.ParameterInfo.Name.ToString());
for (const FVectorParameterValue& P : MI->VectorParameterValues)
OverriddenVectors.Add(P.ParameterInfo.Name.ToString());
for (const FTextureParameterValue& P : MI->TextureParameterValues)
OverriddenTextures.Add(P.ParameterInfo.Name.ToString());
{
FStaticParameterSet SP;
MI->GetStaticParameterValues(SP);
for (const FStaticSwitchParameter& P : SP.StaticSwitchParameters)
if (P.bOverride)
OverriddenStaticSwitches.Add(P.ParameterInfo.Name.ToString());
}
// Overridden parameters
bool bHasOverrides = false; bool bHasOverrides = false;
auto EnsureOverrideHeader = [&]() { for (auto& [Info, Meta] : AllParams)
{
if (!Meta.bOverride) continue;
if (!bHasOverrides) { Result.Append(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; } if (!bHasOverrides) { Result.Append(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; }
}; MCPUtils::FormatMaterialParameter(Result, Info, Meta);
for (const FScalarParameterValue& P : MI->ScalarParameterValues)
{
EnsureOverrideHeader();
Result.Appendf(TEXT(" Scalar \"%s\" = %g\n"), *P.ParameterInfo.Name.ToString(), P.ParameterValue);
}
for (const FVectorParameterValue& P : MI->VectorParameterValues)
{
EnsureOverrideHeader();
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)
{
EnsureOverrideHeader();
Result.Appendf(TEXT(" Texture \"%s\" = %s\n"),
*P.ParameterInfo.Name.ToString(),
P.ParameterValue ? *MCPUtils::FormatName(P.ParameterValue) : TEXT("None"));
}
{
FStaticParameterSet StaticParams;
MI->GetStaticParameterValues(StaticParams);
for (const FStaticSwitchParameter& P : StaticParams.StaticSwitchParameters)
{
if (!P.bOverride) continue;
EnsureOverrideHeader();
Result.Appendf(TEXT(" StaticSwitch \"%s\" = %s\n"),
*P.ParameterInfo.Name.ToString(), P.Value ? TEXT("true") : TEXT("false"));
}
} }
// Inherited (non-overridden) parameters from the base material // Inherited (non-overridden) parameters.
UMaterial* BaseMat = MI->GetMaterial();
if (!BaseMat) return;
bool bHasInherited = false; bool bHasInherited = false;
auto EnsureInheritedHeader = [&]() { for (auto& [Info, Meta] : AllParams)
if (!bHasInherited) { Result.Append(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; }
};
for (UMaterialExpression* Expr : BaseMat->GetExpressions())
{ {
if (!Expr) continue; if (Meta.bOverride) continue;
if (!bHasInherited) { Result.Append(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; }
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr)) MCPUtils::FormatMaterialParameter(Result, Info, Meta);
{
if (OverriddenScalars.Contains(SP->ParameterName.ToString())) continue;
EnsureInheritedHeader();
Result.Appendf(TEXT(" Scalar \"%s\" default=%g\n"), *SP->ParameterName.ToString(), SP->DefaultValue);
}
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
{
if (OverriddenVectors.Contains(VP->ParameterName.ToString())) continue;
EnsureInheritedHeader();
Result.Appendf(TEXT(" Vector \"%s\" default=(%.3f, %.3f, %.3f, %.3f)\n"),
*VP->ParameterName.ToString(),
VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A);
}
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
{
if (OverriddenTextures.Contains(TP->ParameterName.ToString())) continue;
EnsureInheritedHeader();
Result.Appendf(TEXT(" Texture \"%s\" default=%s\n"),
*TP->ParameterName.ToString(),
TP->Texture ? *MCPUtils::FormatName(TP->Texture) : TEXT("None"));
}
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
{
if (OverriddenStaticSwitches.Contains(SSP->ParameterName.ToString())) continue;
EnsureInheritedHeader();
Result.Appendf(TEXT(" StaticSwitch \"%s\" default=%s\n"),
*SSP->ParameterName.ToString(), SSP->DefaultValue ? TEXT("true") : TEXT("false"));
}
} }
} }
}; };

View File

@@ -5,6 +5,7 @@
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/MaterialInstanceConstant.h" #include "Materials/MaterialInstanceConstant.h"
#include "MaterialTypes.h"
#include "Misc/DefaultValueHelper.h" #include "Misc/DefaultValueHelper.h"
#include "MaterialInstance_SetParameter.generated.h" #include "MaterialInstance_SetParameter.generated.h"
@@ -25,6 +26,12 @@ public:
UPROPERTY(meta=(Description="Parameter name to set")) UPROPERTY(meta=(Description="Parameter name to set"))
FString Parameter; FString Parameter;
UPROPERTY(meta=(Description="Parameter association: 'Global', 'Layer', or 'Blend'. Default: 'Global'", Optional))
FString ParameterAssociation = TEXT("Global");
UPROPERTY(meta=(Description="Layer/blend index (0-based). Only used when ParameterAssociation is 'Layer' or 'Blend'", Optional))
int32 ParameterLayer = INDEX_NONE;
UPROPERTY(meta=(Description="Value to set (uses Unreal text format, e.g. '0.5' for scalar, '(R=1,G=0,B=0,A=1)' for vector)")) UPROPERTY(meta=(Description="Value to set (uses Unreal text format, e.g. '0.5' for scalar, '(R=1,G=0,B=0,A=1)' for vector)"))
FString Value; FString Value;
@@ -39,38 +46,30 @@ public:
UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>(); UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>();
if (!MI) return; if (!MI) return;
auto Parameters = MCPUtils::MaterialParameters(MI); // Parse the association string.
EMaterialParameterAssociation Association;
if (!MCPUtils::ParseMaterialParameterAssociation(ParameterAssociation, Association, Result))
return;
// Find a parameter with a matching name. // Build the parameter ID to look up.
FName Name(*Parameter); FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);
MCPMaterialParameter* Found = nullptr;
int MatchCount = 0; // Find it in the material's parameter map.
for (auto& P : Parameters) auto AllParams = MCPUtils::GetMaterialParameters(MI);
FMaterialParameterMetadata* Found = AllParams.Find(ParamID);
if (!Found)
{ {
if (P.Name == Name) Result.Appendf(TEXT("No parameter named '%s' with association=%s layer=%d"),
{ *Parameter, *ParameterAssociation, ParameterLayer);
MatchCount++;
Found = &P;
}
}
if (MatchCount > 1)
{
Result.Appendf(TEXT("More than one parameter named '%s'"), *Parameter);
return; return;
} }
if (MatchCount == 0) if (Found->PrimitiveDataIndex != INDEX_NONE)
{
Result.Appendf(TEXT("No parameter named '%s'"), *Parameter);
return;
}
if (Found->Metadata.PrimitiveDataIndex != INDEX_NONE)
{ {
Result.Appendf(TEXT("Parameter '%s' uses custom primitive data and cannot be set on a material instance"), *Parameter); Result.Appendf(TEXT("Parameter '%s' uses custom primitive data and cannot be set on a material instance"), *Parameter);
return; return;
} }
FMaterialParameterInfo Info(*Parameter); EMaterialParameterType Type = Found->Value.Type;
EMaterialParameterType Type = Found->Metadata.Value.Type;
F.PreEdit(); F.PreEdit();
switch (Type) switch (Type)
@@ -83,7 +82,7 @@ public:
Result.Appendf(TEXT("Failed to parse '%s' as a float"), *Value); Result.Appendf(TEXT("Failed to parse '%s' as a float"), *Value);
return; return;
} }
MI->SetScalarParameterValueEditorOnly(Info, ScalarValue); MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue);
break; break;
} }
case EMaterialParameterType::Vector: case EMaterialParameterType::Vector:
@@ -94,11 +93,11 @@ public:
Result.Appendf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value); Result.Appendf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value);
return; return;
} }
MI->SetVectorParameterValueEditorOnly(Info, Color); MI->SetVectorParameterValueEditorOnly(ParamID, Color);
break; break;
} }
default: default:
Result.Appendf(TEXT("Unsupported parameter type for '%s'"), *Parameter); Result.Appendf(TEXT("Parameters of type %d (see EMaterialParameterType) are not implemented"), (int)Type);
return; return;
} }
F.PostEdit(); F.PostEdit();

View File

@@ -0,0 +1,43 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "Materials/Material.h"
#include "MaterialTypes.h"
#include "Material_DumpParameters.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UMCP_Material_DumpParameters : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Material path"))
FString Path;
virtual FString GetDescription() const override
{
return TEXT("List all parameters on a Material, showing their default values.");
}
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{
MCPFetcher F(Result);
UMaterial* Mat = F.Asset(Path).Cast<UMaterial>();
if (!Mat) return;
auto AllParams = MCPUtils::GetMaterialParameters(Mat);
for (auto& [Info, Meta] : AllParams)
{
MCPUtils::FormatMaterialParameter(Result, Info, Meta);
}
}
};

View File

@@ -1,206 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPUtils.h"
#include "Materials/Material.h"
#include "Materials/MaterialInterface.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialExpressionScalarParameter.h"
#include "Materials/MaterialExpressionVectorParameter.h"
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
#include "Engine/Texture.h"
#include "Material_SetInstanceParameter.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UMCP_Material_SetInstanceParameter : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Material Instance name or path"))
FString MaterialInstance;
UPROPERTY(meta=(Description="Parameter name to set"))
FString ParameterName;
UPROPERTY(meta=(Description="Value to set (number for scalar, object with r/g/b/a for vector, string path for texture, bool for staticSwitch)"))
FMCPJsonObject Value;
UPROPERTY(meta=(Optional, Description="Parameter type: scalar, vector, texture, staticSwitch. Auto-detected from parent if omitted."))
FString Type;
UPROPERTY(meta=(Optional, Description="If true, validate without applying changes"))
bool DryRun = false;
virtual FString GetDescription() const override
{
return TEXT("Set a parameter override on a Material Instance. "
"Supports scalar, vector, texture, and static switch parameter types.");
}
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{
if (!Json->HasField(TEXT("value")))
{
Result.Append(TEXT("ERROR: Missing required field: value\n"));
return;
}
// Load the Material Instance
MCPAssets<UMaterialInstanceConstant> Assets;
if (!Assets.Exact(MaterialInstance).Errors(Result).ENone().ETwo().Load()) return;
UMaterialInstanceConstant* MI = Assets.Object();
// Determine the parameter type -- explicit or auto-detect from parent
FString TypeStr = Type;
if (TypeStr.IsEmpty())
TypeStr = AutoDetectType(MI);
if (TypeStr.IsEmpty())
{
Result.Appendf(TEXT("ERROR: Could not determine parameter type for '%s'. Specify the 'type' field explicitly (scalar, vector, texture, staticSwitch).\n"),
*ParameterName);
return;
}
FMaterialParameterInfo ParamInfo(*ParameterName);
FString NewValueDescription;
if (TypeStr.Equals(TEXT("scalar"), ESearchCase::IgnoreCase))
{
double FloatValue = Json->GetNumberField(TEXT("value"));
if (!DryRun)
MI->SetScalarParameterValueEditorOnly(ParamInfo, (float)FloatValue);
NewValueDescription = FString::Printf(TEXT("%g"), FloatValue);
}
else if (TypeStr.Equals(TEXT("vector"), ESearchCase::IgnoreCase))
{
const TSharedPtr<FJsonObject>* ValueObj = nullptr;
if (!Json->TryGetObjectField(TEXT("value"), ValueObj) || !ValueObj || !(*ValueObj).IsValid())
{
Result.Append(TEXT("ERROR: For vector parameters, 'value' must be an object with r, g, b (and optional a) fields.\n"));
return;
}
double R = (*ValueObj)->GetNumberField(TEXT("r"));
double G = (*ValueObj)->GetNumberField(TEXT("g"));
double B = (*ValueObj)->GetNumberField(TEXT("b"));
double A = (*ValueObj)->HasField(TEXT("a")) ? (*ValueObj)->GetNumberField(TEXT("a")) : 1.0;
FLinearColor Color((float)R, (float)G, (float)B, (float)A);
if (!DryRun)
MI->SetVectorParameterValueEditorOnly(ParamInfo, Color);
NewValueDescription = FString::Printf(TEXT("(%.3f, %.3f, %.3f, %.3f)"), R, G, B, A);
}
else if (TypeStr.Equals(TEXT("texture"), ESearchCase::IgnoreCase))
{
FString TexturePath = Json->GetStringField(TEXT("value"));
if (TexturePath.IsEmpty())
{
Result.Append(TEXT("ERROR: For texture parameters, 'value' must be a texture asset path string.\n"));
return;
}
MCPAssets<UTexture> TexAssets;
if (!TexAssets.Exact(TexturePath).AllContent().Errors(Result).ENone().ETwo().Load()) return;
UTexture* TextureObj = TexAssets.Object();
if (!DryRun)
MI->SetTextureParameterValueEditorOnly(ParamInfo, TextureObj);
NewValueDescription = MCPUtils::FormatName(TextureObj);
}
else if (TypeStr.Equals(TEXT("staticSwitch"), ESearchCase::IgnoreCase))
{
bool bSwitchValue = Json->GetBoolField(TEXT("value"));
if (!DryRun)
{
FStaticParameterSet StaticParams;
MI->GetStaticParameterValues(StaticParams);
bool bFound = false;
for (FStaticSwitchParameter& Param : StaticParams.StaticSwitchParameters)
{
if (Param.ParameterInfo.Name == FName(*ParameterName))
{
Param.Value = bSwitchValue;
Param.bOverride = true;
bFound = true;
break;
}
}
if (!bFound)
{
FStaticSwitchParameter NewParam;
NewParam.ParameterInfo.Name = FName(*ParameterName);
NewParam.Value = bSwitchValue;
NewParam.bOverride = true;
StaticParams.StaticSwitchParameters.Add(NewParam);
}
MI->UpdateStaticPermutation(StaticParams);
}
NewValueDescription = bSwitchValue ? TEXT("true") : TEXT("false");
}
else
{
Result.Appendf(TEXT("ERROR: Unknown parameter type '%s'. Valid types: scalar, vector, texture, staticSwitch\n"), *TypeStr);
return;
}
if (!DryRun)
{
MCPUtils::PreEdit({MI});
MCPUtils::PostEdit({MI});
MCPUtils::SaveGenericPackage(MI);
}
if (DryRun)
Result.Appendf(TEXT("[DRY RUN] Would set %s \"%s\" = %s on %s\n"), *TypeStr, *ParameterName, *NewValueDescription, *MCPUtils::FormatName(MI));
else
Result.Appendf(TEXT("Set %s \"%s\" = %s on %s\n"), *TypeStr, *ParameterName, *NewValueDescription, *MCPUtils::FormatName(MI));
}
private:
// Auto-detect parameter type by examining the base material's expressions.
FString AutoDetectType(UMaterialInstanceConstant* MI)
{
UMaterial* BaseMat = MI->GetMaterial();
if (!BaseMat) return FString();
for (UMaterialExpression* Expr : BaseMat->GetExpressions())
{
if (!Expr) continue;
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr))
{
if (SP->ParameterName.ToString() == ParameterName)
return TEXT("scalar");
}
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
{
if (VP->ParameterName.ToString() == ParameterName)
return TEXT("vector");
}
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
{
if (TP->ParameterName.ToString() == ParameterName)
return TEXT("texture");
}
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
{
if (SSP->ParameterName.ToString() == ParameterName)
return TEXT("staticSwitch");
}
}
return FString();
}
};

View File

@@ -1150,21 +1150,79 @@ void MCPUtils::PostEdit(const TArray<UObject*>& Objects)
GEditor->RedrawAllViewports(); GEditor->RedrawAllViewports();
} }
TArray<MCPMaterialParameter> MCPUtils::MaterialParameters(UMaterialInstanceConstant *MI)
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> MCPUtils::GetMaterialParameters(UMaterialInterface* Material)
{ {
TArray<MCPMaterialParameter> Results; TMap<FMaterialParameterInfo, FMaterialParameterMetadata> Result;
UMaterial* BaseMat = MI->GetMaterial(); if (!Material) return Result;
if (!BaseMat) return Results; TMap<FMaterialParameterInfo, FMaterialParameterMetadata> Temp;
for (UMaterialExpression* Expr : BaseMat->GetExpressions()) for (int32 i = 0; i < (int32)EMaterialParameterType::NumRuntime; i++)
{ {
if (!Expr || !Expr->bIsParameterExpression) continue; Material->GetAllParametersOfType((EMaterialParameterType)i, Temp);
MCPMaterialParameter P; Result.Append(Temp);
P.Name = Expr->GetParameterName(); }
if (P.Name.IsNone()) continue; return Result;
if (!Expr->GetParameterValue(P.Metadata)) continue; }
Results.Add(P);
bool MCPUtils::ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation, MCPErrorCallback Error)
{
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
{
Error.SetError(FString::Printf(TEXT("Invalid ParameterAssociation '%s' (expected 'Global', 'Layer', or 'Blend')"), *Str));
return false;
}
return true;
}
void MCPUtils::FormatMaterialParameter(FStringBuilderBase& Result, 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:
Result.Appendf(TEXT(" %sScalar \"%s\" = %g\n"), *Prefix, *Info.Name.ToString(), Meta.Value.AsScalar());
break;
case EMaterialParameterType::Vector:
{
FLinearColor C = Meta.Value.AsLinearColor();
Result.Appendf(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();
Result.Appendf(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());
Result.Appendf(TEXT(" %sTexture \"%s\" = %s\n"),
*Prefix, *Info.Name.ToString(), Tex ? *MCPUtils::FormatName(Tex) : TEXT("None"));
break;
}
case EMaterialParameterType::StaticSwitch:
Result.Appendf(TEXT(" %sStaticSwitch \"%s\" = %s\n"),
*Prefix, *Info.Name.ToString(), Meta.Value.AsStaticSwitch() ? TEXT("true") : TEXT("false"));
break;
default:
Result.Appendf(TEXT(" %sType%d \"%s\"\n"), *Prefix, (int)Meta.Value.Type, *Info.Name.ToString());
break;
} }
return Results;
} }
bool MCPUtils::SaveMaterialPackage(UMaterial* Material) bool MCPUtils::SaveMaterialPackage(UMaterial* Material)

View File

@@ -98,12 +98,6 @@ struct MCPErrorCallback
void SetError(const FString& Msg) const { Func(Msg); } void SetError(const FString& Msg) const { Func(Msg); }
}; };
struct MCPMaterialParameter
{
FName Name;
FMaterialParameterMetadata Metadata;
};
// Stateless utility functions used by MCP handlers and the MCP server. // Stateless utility functions used by MCP handlers and the MCP server.
// This is effectively a namespace — all methods are static. // This is effectively a namespace — all methods are static.
class MCPUtils class MCPUtils
@@ -285,8 +279,10 @@ public:
static void PreEdit(const TArray<UObject*>& Objects); static void PreEdit(const TArray<UObject*>& Objects);
static void PostEdit(const TArray<UObject*>& Objects); static void PostEdit(const TArray<UObject*>& Objects);
// ----- Material Instance ----- // ----- Material Parameters -----
static TArray<MCPMaterialParameter> MaterialParameters(UMaterialInstanceConstant *MI); static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material);
static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation, MCPErrorCallback Error = nullptr);
static void FormatMaterialParameter(FStringBuilderBase& Result, const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
// ----- Editable template ----- // ----- Editable template -----
// Given an object, returns the appropriate template object for generic // Given an object, returns the appropriate template object for generic