229 lines
8.5 KiB
C++
229 lines
8.5 KiB
C++
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "MCPHandler.h"
|
|
#include "MCPAssets.h"
|
|
#include "MCPUtils.h"
|
|
#include "Materials/Material.h"
|
|
#include "MaterialDomain.h"
|
|
#include "Materials/MaterialInstanceConstant.h"
|
|
#include "Materials/MaterialExpression.h"
|
|
#include "Materials/MaterialExpressionScalarParameter.h"
|
|
#include "Materials/MaterialExpressionVectorParameter.h"
|
|
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
|
|
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
|
|
#include "Materials/MaterialExpressionTextureSample.h"
|
|
#include "Engine/Texture.h"
|
|
#include "UMCPHandler_DumpMaterial.generated.h"
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------
|
|
|
|
UCLASS(meta=(Group="Unclassified"))
|
|
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, FStringBuilderBase& Result) override
|
|
{
|
|
MCPAssets<UMaterialInterface> Assets;
|
|
Assets.NoScans();
|
|
Assets.Scan<UMaterial>();
|
|
Assets.Scan<UMaterialInstanceConstant>();
|
|
if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
|
UMaterialInterface* LoadedObj = Assets.Object();
|
|
|
|
if (UMaterial* Mat = Cast<UMaterial>(LoadedObj))
|
|
{
|
|
EmitMaterial(Mat, Result);
|
|
return;
|
|
}
|
|
|
|
if (UMaterialInstanceConstant* MI = Cast<UMaterialInstanceConstant>(LoadedObj))
|
|
{
|
|
EmitMaterialInstance(MI, Result);
|
|
return;
|
|
}
|
|
|
|
Result.Appendf(TEXT("ERROR: Loaded object is %s, expected Material or MaterialInstance.\n"),
|
|
*LoadedObj->GetClass()->GetName());
|
|
}
|
|
|
|
private:
|
|
void EmitMaterial(UMaterial* Mat, FStringBuilderBase& Result)
|
|
{
|
|
Result.Appendf(TEXT("Material: %s\n"), *MCPUtils::FormatName(Mat));
|
|
Result.Appendf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(Mat->MaterialDomain, TEXT("MD_")));
|
|
Result.Appendf(TEXT("BlendMode: %s\n"), *MCPUtils::EnumToString(Mat->BlendMode, TEXT("BLEND_")));
|
|
Result.Appendf(TEXT("TwoSided: %s\n"), Mat->IsTwoSided() ? TEXT("true") : TEXT("false"));
|
|
|
|
// Shading models
|
|
FMaterialShadingModelField SMField = Mat->GetShadingModels();
|
|
const UEnum* SMEnum = StaticEnum<EMaterialShadingModel>();
|
|
TArray<FString> SMNames;
|
|
for (int32 i = 0; i < SMEnum->NumEnums() - 1; ++i)
|
|
{
|
|
EMaterialShadingModel SM = (EMaterialShadingModel)SMEnum->GetValueByIndex(i);
|
|
if (SMField.HasShadingModel(SM))
|
|
SMNames.Add(SMEnum->GetNameStringByIndex(i));
|
|
}
|
|
Result.Appendf(TEXT("ShadingModels: %s\n"), *FString::Join(SMNames, TEXT(", ")));
|
|
|
|
// Parameters
|
|
auto Expressions = Mat->GetExpressions();
|
|
bool bHasParams = false;
|
|
for (UMaterialExpression* Expr : Expressions)
|
|
{
|
|
if (!Expr) continue;
|
|
|
|
if (auto* SP = Cast<UMaterialExpressionScalarParameter>(Expr))
|
|
{
|
|
if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; }
|
|
Result.Appendf(TEXT(" Scalar \"%s\" = %g"), *SP->ParameterName.ToString(), SP->DefaultValue);
|
|
if (!SP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SP->Group.ToString());
|
|
Result.Append(TEXT("\n"));
|
|
}
|
|
else if (auto* VP = Cast<UMaterialExpressionVectorParameter>(Expr))
|
|
{
|
|
if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; }
|
|
Result.Appendf(TEXT(" Vector \"%s\" = (%.3f, %.3f, %.3f, %.3f)"),
|
|
*VP->ParameterName.ToString(),
|
|
VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A);
|
|
if (!VP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *VP->Group.ToString());
|
|
Result.Append(TEXT("\n"));
|
|
}
|
|
else if (auto* TP = Cast<UMaterialExpressionTextureSampleParameter2D>(Expr))
|
|
{
|
|
if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; }
|
|
Result.Appendf(TEXT(" Texture \"%s\" = %s"),
|
|
*TP->ParameterName.ToString(),
|
|
TP->Texture ? *MCPUtils::FormatName(TP->Texture) : TEXT("None"));
|
|
if (!TP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *TP->Group.ToString());
|
|
Result.Append(TEXT("\n"));
|
|
}
|
|
else if (auto* SSP = Cast<UMaterialExpressionStaticSwitchParameter>(Expr))
|
|
{
|
|
if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; }
|
|
Result.Appendf(TEXT(" StaticSwitch \"%s\" = %s"),
|
|
*SSP->ParameterName.ToString(),
|
|
SSP->DefaultValue ? TEXT("true") : TEXT("false"));
|
|
if (!SSP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SSP->Group.ToString());
|
|
Result.Append(TEXT("\n"));
|
|
}
|
|
}
|
|
|
|
// Referenced textures
|
|
auto RefTexObjs = Mat->GetReferencedTextures();
|
|
bool bHasTextures = false;
|
|
for (const TObjectPtr<UObject>& TexObj : RefTexObjs)
|
|
{
|
|
if (!TexObj) continue;
|
|
if (!bHasTextures) { Result.Append(TEXT("\nReferenced Textures:\n")); bHasTextures = true; }
|
|
if (UTexture* Tex = Cast<UTexture>(TexObj.Get()))
|
|
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Tex));
|
|
else
|
|
Result.Appendf(TEXT(" %s\n"), *TexObj->GetPathName());
|
|
}
|
|
|
|
// Usage flags — only print enabled ones
|
|
Result.Append(TEXT("\nUsage Flags:"));
|
|
bool bAnyUsage = false;
|
|
auto EmitFlag = [&](bool bSet, const TCHAR* Name) {
|
|
if (bSet) { Result.Appendf(TEXT(" %s"), Name); bAnyUsage = true; }
|
|
};
|
|
EmitFlag(Mat->bUsedWithSkeletalMesh, TEXT("SkeletalMesh"));
|
|
EmitFlag(Mat->bUsedWithMorphTargets, TEXT("MorphTargets"));
|
|
EmitFlag(Mat->bUsedWithNiagaraSprites, TEXT("NiagaraSprites"));
|
|
EmitFlag(Mat->bUsedWithParticleSprites, TEXT("ParticleSprites"));
|
|
EmitFlag(Mat->bUsedWithStaticLighting, TEXT("StaticLighting"));
|
|
if (!bAnyUsage) Result.Append(TEXT(" (none)"));
|
|
Result.Append(TEXT("\n"));
|
|
|
|
// Stats
|
|
Result.Appendf(TEXT("Expressions: %d\n"), Expressions.Num());
|
|
int32 TextureSampleCount = 0;
|
|
for (UMaterialExpression* Expr : Expressions)
|
|
if (Expr && Expr->IsA<UMaterialExpressionTextureSample>())
|
|
TextureSampleCount++;
|
|
Result.Appendf(TEXT("TextureSamples: %d\n"), TextureSampleCount);
|
|
if (Mat->MaterialGraph)
|
|
Result.Appendf(TEXT("GraphNodes: %d\n"), Mat->MaterialGraph->Nodes.Num());
|
|
|
|
// Additional settings — only print non-default values
|
|
if (Mat->OpacityMaskClipValue != 0.3333f)
|
|
Result.Appendf(TEXT("OpacityMaskClipValue: %g\n"), Mat->OpacityMaskClipValue);
|
|
if (Mat->DitheredLODTransition)
|
|
Result.Append(TEXT("DitheredLODTransition: true\n"));
|
|
if (Mat->bAllowNegativeEmissiveColor)
|
|
Result.Append(TEXT("AllowNegativeEmissiveColor: true\n"));
|
|
}
|
|
|
|
void EmitMaterialInstance(UMaterialInstanceConstant* MI, FStringBuilderBase& Result)
|
|
{
|
|
Result.Appendf(TEXT("MaterialInstance: %s\n"), *MCPUtils::FormatName(MI));
|
|
if (MI->Parent)
|
|
{
|
|
if (UMaterial* ParentMat = Cast<UMaterial>(MI->Parent))
|
|
Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMat));
|
|
else if (UMaterialInstance* ParentMI = Cast<UMaterialInstance>(MI->Parent))
|
|
Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMI));
|
|
else
|
|
Result.Appendf(TEXT("Parent: %s\n"), *MI->Parent->GetPathName());
|
|
}
|
|
|
|
// Overridden parameters
|
|
bool bHasParams = false;
|
|
auto EnsureHeader = [&]() {
|
|
if (!bHasParams) { Result.Append(TEXT("\nOverridden Parameters:\n")); bHasParams = true; }
|
|
};
|
|
|
|
for (const FScalarParameterValue& P : MI->ScalarParameterValues)
|
|
{
|
|
EnsureHeader();
|
|
Result.Appendf(TEXT(" Scalar \"%s\" = %g\n"), *P.ParameterInfo.Name.ToString(), P.ParameterValue);
|
|
}
|
|
|
|
for (const FVectorParameterValue& P : MI->VectorParameterValues)
|
|
{
|
|
EnsureHeader();
|
|
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)
|
|
{
|
|
EnsureHeader();
|
|
if (P.ParameterValue)
|
|
{
|
|
Result.Appendf(TEXT(" Texture \"%s\" = %s\n"),
|
|
*P.ParameterInfo.Name.ToString(), *MCPUtils::FormatName(P.ParameterValue));
|
|
}
|
|
else
|
|
{
|
|
Result.Appendf(TEXT(" Texture \"%s\" = None\n"), *P.ParameterInfo.Name.ToString());
|
|
}
|
|
}
|
|
|
|
for (const FStaticSwitchParameter& P : MI->GetStaticParameters().StaticSwitchParameters)
|
|
{
|
|
EnsureHeader();
|
|
Result.Appendf(TEXT(" StaticSwitch \"%s\" = %s%s\n"),
|
|
*P.ParameterInfo.Name.ToString(),
|
|
P.Value ? TEXT("true") : TEXT("false"),
|
|
P.bOverride ? TEXT("") : TEXT(" (not overridden)"));
|
|
}
|
|
}
|
|
};
|