294 lines
12 KiB
C++
294 lines
12 KiB
C++
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "MCPHandler.h"
|
|
#include "MCPAssetFinder.h"
|
|
#include "MCPUtils.h"
|
|
#include "Materials/Material.h"
|
|
#include "MaterialDomain.h"
|
|
#include "Materials/MaterialInstanceConstant.h"
|
|
#include "Materials/MaterialFunction.h"
|
|
#include "Materials/MaterialExpression.h"
|
|
#include "Materials/MaterialExpressionScalarParameter.h"
|
|
#include "Materials/MaterialExpressionVectorParameter.h"
|
|
#include "Materials/MaterialExpressionTextureObjectParameter.h"
|
|
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
|
|
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
|
|
#include "Materials/MaterialExpressionConstant.h"
|
|
#include "Materials/MaterialExpressionConstant3Vector.h"
|
|
#include "Materials/MaterialExpressionConstant4Vector.h"
|
|
#include "Materials/MaterialExpressionTextureSample.h"
|
|
#include "Materials/MaterialExpressionTextureCoordinate.h"
|
|
#include "Materials/MaterialExpressionComponentMask.h"
|
|
#include "Materials/MaterialExpressionCustom.h"
|
|
#include "Materials/MaterialExpressionFunctionInput.h"
|
|
#include "Materials/MaterialExpressionFunctionOutput.h"
|
|
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
|
|
#include "MaterialGraph/MaterialGraph.h"
|
|
#include "MaterialGraph/MaterialGraphNode.h"
|
|
#include "MaterialGraph/MaterialGraphNode_Root.h"
|
|
#include "MaterialGraph/MaterialGraphSchema.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "AssetRegistry/IAssetRegistry.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "UMCPHandler_DumpMaterial.generated.h"
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------
|
|
|
|
UCLASS()
|
|
class UMCPHandler_DumpMaterial : public UObject, public IMCPHandler
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UPROPERTY(meta=(Description="Material or MaterialInstance name or package path"))
|
|
FString Material;
|
|
|
|
virtual FString GetDescription() const override
|
|
{
|
|
return TEXT("Get detailed info about a material or material instance, including parameters, usage flags, and referenced textures.");
|
|
}
|
|
|
|
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override
|
|
{
|
|
FString DecodedName = MCPUtils::UrlDecode(Material);
|
|
|
|
// Try loading as UMaterial or UMaterialInstanceConstant
|
|
MCPAssets<UMaterialInterface> Assets;
|
|
Assets.Scan(UMaterial::StaticClass());
|
|
Assets.Scan(UMaterialInstanceConstant::StaticClass());
|
|
if (!Assets.Exact(DecodedName).Errors(Result).ENone().ETwo().Load()) return;
|
|
UMaterialInterface* LoadedObj = Assets.Object();
|
|
|
|
if (UMaterial* MaterialObj = Cast<UMaterial>(LoadedObj))
|
|
{
|
|
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: GetMaterial — loaded material '%s'"), *MaterialObj->GetName());
|
|
|
|
Result->SetStringField(TEXT("name"), MaterialObj->GetName());
|
|
Result->SetStringField(TEXT("path"), MaterialObj->GetPathName());
|
|
Result->SetStringField(TEXT("type"), TEXT("Material"));
|
|
|
|
// Material domain
|
|
FString DomainStr = TEXT("Unknown");
|
|
if (const UEnum* DomainEnum = StaticEnum<EMaterialDomain>())
|
|
{
|
|
DomainStr = DomainEnum->GetNameStringByValue((int64)MaterialObj->MaterialDomain);
|
|
}
|
|
Result->SetStringField(TEXT("domain"), DomainStr);
|
|
|
|
// Blend mode
|
|
FString BlendModeStr = TEXT("Unknown");
|
|
if (const UEnum* BlendEnum = StaticEnum<EBlendMode>())
|
|
{
|
|
BlendModeStr = BlendEnum->GetNameStringByValue((int64)MaterialObj->BlendMode);
|
|
}
|
|
Result->SetStringField(TEXT("blendMode"), BlendModeStr);
|
|
|
|
// Shading models
|
|
TArray<TSharedPtr<FJsonValue>> ShadingModels;
|
|
FMaterialShadingModelField SMField = MaterialObj->GetShadingModels();
|
|
if (const UEnum* SMEnum = StaticEnum<EMaterialShadingModel>())
|
|
{
|
|
for (int32 i = 0; i < SMEnum->NumEnums() - 1; ++i)
|
|
{
|
|
EMaterialShadingModel SM = (EMaterialShadingModel)SMEnum->GetValueByIndex(i);
|
|
if (SMField.HasShadingModel(SM))
|
|
{
|
|
ShadingModels.Add(MakeShared<FJsonValueString>(SMEnum->GetNameStringByIndex(i)));
|
|
}
|
|
}
|
|
}
|
|
Result->SetArrayField(TEXT("shadingModels"), ShadingModels);
|
|
|
|
// Two-sided
|
|
Result->SetBoolField(TEXT("twoSided"), MaterialObj->IsTwoSided());
|
|
|
|
// Expression count
|
|
auto Expressions = MaterialObj->GetExpressions();
|
|
Result->SetNumberField(TEXT("expressionCount"), Expressions.Num());
|
|
|
|
// Parameters — iterate expressions for parameter types
|
|
TArray<TSharedPtr<FJsonValue>> Parameters;
|
|
for (UMaterialExpression* Expr : Expressions)
|
|
{
|
|
if (!Expr) continue;
|
|
|
|
TSharedRef<FJsonObject> ParamObj = MakeShared<FJsonObject>();
|
|
bool bIsParam = false;
|
|
|
|
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr))
|
|
{
|
|
bIsParam = true;
|
|
ParamObj->SetStringField(TEXT("name"), SP->ParameterName.ToString());
|
|
ParamObj->SetStringField(TEXT("type"), TEXT("Scalar"));
|
|
ParamObj->SetStringField(TEXT("group"), SP->Group.ToString());
|
|
ParamObj->SetNumberField(TEXT("defaultValue"), SP->DefaultValue);
|
|
}
|
|
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
|
|
{
|
|
bIsParam = true;
|
|
ParamObj->SetStringField(TEXT("name"), VP->ParameterName.ToString());
|
|
ParamObj->SetStringField(TEXT("type"), TEXT("Vector"));
|
|
ParamObj->SetStringField(TEXT("group"), VP->Group.ToString());
|
|
TSharedRef<FJsonObject> DefVal = MakeShared<FJsonObject>();
|
|
DefVal->SetNumberField(TEXT("r"), VP->DefaultValue.R);
|
|
DefVal->SetNumberField(TEXT("g"), VP->DefaultValue.G);
|
|
DefVal->SetNumberField(TEXT("b"), VP->DefaultValue.B);
|
|
DefVal->SetNumberField(TEXT("a"), VP->DefaultValue.A);
|
|
ParamObj->SetObjectField(TEXT("defaultValue"), DefVal);
|
|
}
|
|
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
|
|
{
|
|
bIsParam = true;
|
|
ParamObj->SetStringField(TEXT("name"), TP->ParameterName.ToString());
|
|
ParamObj->SetStringField(TEXT("type"), TEXT("Texture"));
|
|
ParamObj->SetStringField(TEXT("group"), TP->Group.ToString());
|
|
if (TP->Texture)
|
|
ParamObj->SetStringField(TEXT("defaultValue"), TP->Texture->GetPathName());
|
|
}
|
|
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
|
|
{
|
|
bIsParam = true;
|
|
ParamObj->SetStringField(TEXT("name"), SSP->ParameterName.ToString());
|
|
ParamObj->SetStringField(TEXT("type"), TEXT("StaticSwitch"));
|
|
ParamObj->SetStringField(TEXT("group"), SSP->Group.ToString());
|
|
ParamObj->SetBoolField(TEXT("defaultValue"), SSP->DefaultValue);
|
|
}
|
|
|
|
if (bIsParam)
|
|
{
|
|
Parameters.Add(MakeShared<FJsonValueObject>(ParamObj));
|
|
}
|
|
}
|
|
Result->SetArrayField(TEXT("parameters"), Parameters);
|
|
|
|
// Referenced textures
|
|
TArray<TSharedPtr<FJsonValue>> ReferencedTextures;
|
|
auto RefTexObjs = MaterialObj->GetReferencedTextures();
|
|
for (const TObjectPtr<UObject>& TexObj : RefTexObjs)
|
|
{
|
|
if (TexObj)
|
|
{
|
|
ReferencedTextures.Add(MakeShared<FJsonValueString>(TexObj->GetPathName()));
|
|
}
|
|
}
|
|
Result->SetArrayField(TEXT("referencedTextures"), ReferencedTextures);
|
|
|
|
// Graph node count
|
|
int32 GraphNodeCount = 0;
|
|
if (MaterialObj->MaterialGraph)
|
|
{
|
|
GraphNodeCount = MaterialObj->MaterialGraph->Nodes.Num();
|
|
}
|
|
Result->SetNumberField(TEXT("graphNodeCount"), GraphNodeCount);
|
|
|
|
// Usage flags
|
|
TSharedRef<FJsonObject> UsageFlags = MakeShared<FJsonObject>();
|
|
UsageFlags->SetBoolField(TEXT("bUsedWithSkeletalMesh"), MaterialObj->bUsedWithSkeletalMesh != 0);
|
|
UsageFlags->SetBoolField(TEXT("bUsedWithMorphTargets"), MaterialObj->bUsedWithMorphTargets != 0);
|
|
UsageFlags->SetBoolField(TEXT("bUsedWithNiagaraSprites"), MaterialObj->bUsedWithNiagaraSprites != 0);
|
|
UsageFlags->SetBoolField(TEXT("bUsedWithParticleSprites"), MaterialObj->bUsedWithParticleSprites != 0);
|
|
UsageFlags->SetBoolField(TEXT("bUsedWithStaticLighting"), MaterialObj->bUsedWithStaticLighting != 0);
|
|
Result->SetObjectField(TEXT("usageFlags"), UsageFlags);
|
|
|
|
// Opacity mask clip value
|
|
Result->SetNumberField(TEXT("opacityMaskClipValue"), MaterialObj->OpacityMaskClipValue);
|
|
|
|
// Additional settings
|
|
Result->SetBoolField(TEXT("ditheredLODTransition"), MaterialObj->DitheredLODTransition != 0);
|
|
Result->SetBoolField(TEXT("bAllowNegativeEmissiveColor"), MaterialObj->bAllowNegativeEmissiveColor != 0);
|
|
|
|
// Texture sample count (simple expression scan)
|
|
int32 TextureSampleCount = 0;
|
|
for (UMaterialExpression* Expr : Expressions)
|
|
{
|
|
if (Expr && Expr->IsA<UMaterialExpressionTextureSample>())
|
|
{
|
|
TextureSampleCount++;
|
|
}
|
|
}
|
|
Result->SetNumberField(TEXT("textureSampleCount"), TextureSampleCount);
|
|
|
|
return;
|
|
}
|
|
|
|
if (UMaterialInstanceConstant* MI = Cast<UMaterialInstanceConstant>(LoadedObj))
|
|
{
|
|
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: GetMaterial — loaded material instance '%s'"), *MI->GetName());
|
|
|
|
Result->SetStringField(TEXT("name"), MI->GetName());
|
|
Result->SetStringField(TEXT("path"), MI->GetPathName());
|
|
Result->SetStringField(TEXT("type"), TEXT("MaterialInstance"));
|
|
|
|
if (MI->Parent)
|
|
{
|
|
Result->SetStringField(TEXT("parent"), MI->Parent->GetName());
|
|
Result->SetStringField(TEXT("parentPath"), MI->Parent->GetPathName());
|
|
}
|
|
|
|
// Overridden parameters
|
|
TArray<TSharedPtr<FJsonValue>> OverriddenParams;
|
|
|
|
// Scalar parameters
|
|
for (const FScalarParameterValue& Param : MI->ScalarParameterValues)
|
|
{
|
|
TSharedRef<FJsonObject> PObj = MakeShared<FJsonObject>();
|
|
PObj->SetStringField(TEXT("name"), Param.ParameterInfo.Name.ToString());
|
|
PObj->SetStringField(TEXT("type"), TEXT("Scalar"));
|
|
PObj->SetNumberField(TEXT("value"), Param.ParameterValue);
|
|
OverriddenParams.Add(MakeShared<FJsonValueObject>(PObj));
|
|
}
|
|
|
|
// Vector parameters
|
|
for (const FVectorParameterValue& Param : MI->VectorParameterValues)
|
|
{
|
|
TSharedRef<FJsonObject> PObj = MakeShared<FJsonObject>();
|
|
PObj->SetStringField(TEXT("name"), Param.ParameterInfo.Name.ToString());
|
|
PObj->SetStringField(TEXT("type"), TEXT("Vector"));
|
|
TSharedRef<FJsonObject> Val = MakeShared<FJsonObject>();
|
|
Val->SetNumberField(TEXT("r"), Param.ParameterValue.R);
|
|
Val->SetNumberField(TEXT("g"), Param.ParameterValue.G);
|
|
Val->SetNumberField(TEXT("b"), Param.ParameterValue.B);
|
|
Val->SetNumberField(TEXT("a"), Param.ParameterValue.A);
|
|
PObj->SetObjectField(TEXT("value"), Val);
|
|
OverriddenParams.Add(MakeShared<FJsonValueObject>(PObj));
|
|
}
|
|
|
|
// Texture parameters
|
|
for (const FTextureParameterValue& Param : MI->TextureParameterValues)
|
|
{
|
|
TSharedRef<FJsonObject> PObj = MakeShared<FJsonObject>();
|
|
PObj->SetStringField(TEXT("name"), Param.ParameterInfo.Name.ToString());
|
|
PObj->SetStringField(TEXT("type"), TEXT("Texture"));
|
|
if (Param.ParameterValue)
|
|
PObj->SetStringField(TEXT("value"), Param.ParameterValue->GetPathName());
|
|
else
|
|
PObj->SetStringField(TEXT("value"), TEXT("None"));
|
|
OverriddenParams.Add(MakeShared<FJsonValueObject>(PObj));
|
|
}
|
|
|
|
// Static switch parameters
|
|
for (const FStaticSwitchParameter& Param : MI->GetStaticParameters().StaticSwitchParameters)
|
|
{
|
|
TSharedRef<FJsonObject> PObj = MakeShared<FJsonObject>();
|
|
PObj->SetStringField(TEXT("name"), Param.ParameterInfo.Name.ToString());
|
|
PObj->SetStringField(TEXT("type"), TEXT("StaticSwitch"));
|
|
PObj->SetBoolField(TEXT("value"), Param.Value);
|
|
PObj->SetBoolField(TEXT("overridden"), Param.bOverride);
|
|
OverriddenParams.Add(MakeShared<FJsonValueObject>(PObj));
|
|
}
|
|
|
|
Result->SetArrayField(TEXT("overriddenParameters"), OverriddenParams);
|
|
|
|
return;
|
|
}
|
|
|
|
MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("Material or MaterialInstance '%s' not found. Use list_materials to see available assets."), *DecodedName));
|
|
}
|
|
};
|