From 9c1f47417048717beb7a7ce01d3fbb6c93f06021 Mon Sep 17 00:00:00 2001 From: jyelon Date: Sun, 5 Apr 2026 03:20:51 -0400 Subject: [PATCH] work on material parameters --- Content/Testing/M_Kaleidoscope.uasset | 4 +- .../MaterialInstance_ClearParameter.h | 43 +--- .../MaterialInstance_DumpParameters.h | 20 +- .../Handlers/MaterialInstance_SetParameter.h | 42 +--- .../Handlers/Material_DumpParameters.h | 6 +- .../UEWingman/Handlers/Test_Sanitizer.h | 32 --- .../UEWingman/Handlers/Test_Tokenizer.h | 33 --- .../UEWingman/Handlers/Test_TypeToText.h | 63 ----- .../UEWingman/Handlers/Test_Unsanitize.h | 41 ---- .../Private/WingMaterialParameter.cpp | 216 ++++++++++++++---- .../UEWingman/Private/WingTokenizer.cpp | 6 +- .../UEWingman/Public/WingMaterialParameter.h | 54 ++++- .../Source/UEWingman/Public/WingTokenizer.h | 4 +- 13 files changed, 257 insertions(+), 307 deletions(-) delete mode 100644 Plugins/UEWingman/Source/UEWingman/Handlers/Test_Sanitizer.h delete mode 100644 Plugins/UEWingman/Source/UEWingman/Handlers/Test_Tokenizer.h delete mode 100644 Plugins/UEWingman/Source/UEWingman/Handlers/Test_TypeToText.h delete mode 100644 Plugins/UEWingman/Source/UEWingman/Handlers/Test_Unsanitize.h diff --git a/Content/Testing/M_Kaleidoscope.uasset b/Content/Testing/M_Kaleidoscope.uasset index bd0ad18e..0341f236 100644 --- a/Content/Testing/M_Kaleidoscope.uasset +++ b/Content/Testing/M_Kaleidoscope.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78b6d2cbe8c2e3fbd2183df29f8d9200a632f74c94586cbd4845ffe1b256e411 -size 15193 +oid sha256:2d66b27936fc64bc064b7e6348878422b688ed05e531200b83fb87be584441d8 +size 16172 diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_ClearParameter.h b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_ClearParameter.h index 54be60cf..7a2cd78f 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_ClearParameter.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_ClearParameter.h @@ -24,56 +24,27 @@ public: UPROPERTY(EditAnywhere, meta=(Description="Target material instance")) FString MaterialInstance; - UPROPERTY(EditAnywhere, meta=(Description="Parameter name to clear")) + UPROPERTY(EditAnywhere, meta=(Description="Parameter ID: 'Name', 'Layer:N:Name', or 'Blend:N:Name'")) FString Parameter; - UPROPERTY(EditAnywhere, meta=(Description="Parameter association: 'Global', 'Layer', or 'Blend'. Default: 'Global'", Optional)) - FString ParameterAssociation = TEXT("Global"); - - UPROPERTY(EditAnywhere, meta=(Description="Layer/blend index (0-based). Only used when ParameterAssociation is 'Layer' or 'Blend'", Optional)) - int32 ParameterLayer = INDEX_NONE; - virtual void Register() override { UWingServer::AddHandler(this, TEXT("Remove a parameter override from a Material Instance, reverting it to the parent material's value.")); } + virtual void Handle() override { WingFetcher F(WingOut::Stdout); UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast(); if (!MI) return; - // Parse the association string. - EMaterialParameterAssociation Association; - if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association, WingOut::Stdout)) + // Parse the parameter ID. + FMaterialParameterInfo ID; + if (!WingMaterialParameter::ParseID(Parameter, ID, WingOut::Stdout)) 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; }); - }; - - 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); - - if (Removed == 0) - { - WingOut::Stdout.Printf(TEXT("No override found for parameter '%s' (association=%s layer=%d) on %s"), - *Parameter, *ParameterAssociation, ParameterLayer, *WingUtils::FormatName(MI)); - return; - } - - WingOut::Stdout.Printf(TEXT("Cleared override for '%s' on %s\n"), - *Parameter, *WingUtils::FormatName(MI)); + if (!WingMaterialParameter::RemoveOverride(ID, MI, WingOut::Stdout)) return + WingOut::Stdout.Printf(TEXT("Removed override\n")); } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_DumpParameters.h b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_DumpParameters.h index 0874c02d..29ec28c0 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_DumpParameters.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_DumpParameters.h @@ -36,23 +36,7 @@ public: if (!MI) return; auto AllParams = WingMaterialParameter::GetMaterialParameters(MI); - - // Overridden parameters first. - bool bHasOverrides = false; - for (auto& [Info, Meta] : AllParams) - { - if (!Meta.bOverride) continue; - if (!bHasOverrides) { WingOut::Stdout.Print(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; } - WingMaterialParameter::FormatMaterialParameter(Info, Meta); - } - - // Inherited (non-overridden) parameters. - bool bHasInherited = false; - for (auto& [Info, Meta] : AllParams) - { - if (Meta.bOverride) continue; - if (!bHasInherited) { WingOut::Stdout.Print(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; } - WingMaterialParameter::FormatMaterialParameter(Info, Meta); - } + WingMaterialParameter::PrintAll(AllParams, true); + if (AllParams.IsEmpty()) WingOut::Stdout.Printf(TEXT("No material parameters.\n")); } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_SetParameter.h b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_SetParameter.h index 9a715e7b..152d72a5 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_SetParameter.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_SetParameter.h @@ -25,15 +25,9 @@ public: UPROPERTY(EditAnywhere, meta=(Description="Target material instance")) FString MaterialInstance; - UPROPERTY(EditAnywhere, meta=(Description="Parameter name to set")) + UPROPERTY(EditAnywhere, meta=(Description="Parameter ID: 'Name', 'Layer:N:Name', or 'Blend:N:Name'")) FString Parameter; - UPROPERTY(EditAnywhere, meta=(Description="Parameter association: 'Global', 'Layer', or 'Blend'. Default: 'Global'", Optional)) - FString ParameterAssociation = TEXT("Global"); - - UPROPERTY(EditAnywhere, meta=(Description="Layer/blend index (0-based). Only used when ParameterAssociation is 'Layer' or 'Blend'", Optional)) - int32 ParameterLayer = INDEX_NONE; - UPROPERTY(EditAnywhere, 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; @@ -48,30 +42,19 @@ public: UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast(); if (!MI) return; - // Parse the association string. - EMaterialParameterAssociation Association; - if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association, WingOut::Stdout)) + // Parse the parameter ID. + FMaterialParameterInfo ID; + if (!WingMaterialParameter::ParseID(Parameter, ID, WingOut::Stdout)) return; - // Build the parameter ID to look up. - FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer); - // Find it in the material's parameter map. auto AllParams = WingMaterialParameter::GetMaterialParameters(MI); - FMaterialParameterMetadata* Found = AllParams.Find(ParamID); - if (!Found) - { - WingOut::Stdout.Printf(TEXT("No parameter named '%s' with association=%s layer=%d"), - *Parameter, *ParameterAssociation, ParameterLayer); - return; - } - if (Found->PrimitiveDataIndex != INDEX_NONE) - { - WingOut::Stdout.Printf(TEXT("Parameter '%s' uses custom primitive data and cannot be set on a material instance"), *Parameter); - return; - } + FMaterialParameterMetadata* Meta = + WingMaterialParameter::FindParameter(AllParams, ID, WingOut::Stdout); + if (!Meta) return; + if (!WingMaterialParameter::CheckNoCustomPrimitiveData(ID, *Meta, WingOut::Stdout)) return; - EMaterialParameterType Type = Found->Value.Type; + EMaterialParameterType Type = Meta->Value.Type; switch (Type) { @@ -83,7 +66,7 @@ public: WingOut::Stdout.Printf(TEXT("Failed to parse '%s' as a float"), *Value); return; } - MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue); + MI->SetScalarParameterValueEditorOnly(ID, ScalarValue); break; } case EMaterialParameterType::Vector: @@ -94,14 +77,13 @@ public: WingOut::Stdout.Printf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value); return; } - MI->SetVectorParameterValueEditorOnly(ParamID, Color); + MI->SetVectorParameterValueEditorOnly(ID, Color); break; } default: WingOut::Stdout.Printf(TEXT("Parameters of type %d (see EMaterialParameterType) are not implemented"), (int)Type); return; } - WingOut::Stdout.Printf(TEXT("Set '%s' = %s on %s\n"), - *Parameter, *Value, *WingUtils::FormatName(MI)); + WingOut::Stdout.Printf(TEXT("Assigned.\n")); } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Material_DumpParameters.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Material_DumpParameters.h index 44ccc05f..2e9468ea 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Material_DumpParameters.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Material_DumpParameters.h @@ -35,11 +35,7 @@ public: if (!Mat) return; auto AllParams = WingMaterialParameter::GetMaterialParameters(Mat); - - for (auto& [Info, Meta] : AllParams) - { - WingMaterialParameter::FormatMaterialParameter(Info, Meta); - } + WingMaterialParameter::PrintAll(AllParams, false); if (AllParams.IsEmpty()) WingOut::Stdout.Printf(TEXT("No material parameters.\n")); } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Sanitizer.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Sanitizer.h deleted file mode 100644 index db24a2bf..00000000 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Sanitizer.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingTokenizer.h" -#include "Test_Sanitizer.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_Test_Sanitizer : public UWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, meta=(Description="The string to sanitize")) - FString Input; - - virtual void Register() override - { - UWingServer::AddHandler(this, - TEXT("Test the sanitizer by sanitizing a string and printing the result.")); - } - virtual void Handle() override - { - WingOut::Stdout.Printf(TEXT("%s\n"), *WingTokenizer::ExternalizeID(FName(Input))); - } -}; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Tokenizer.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Tokenizer.h deleted file mode 100644 index e49de826..00000000 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Tokenizer.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingTokenizer.h" -#include "Test_Tokenizer.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_Test_Tokenizer : public UWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, meta=(Description="The string to tokenize")) - FString Input; - - virtual void Register() override - { - UWingServer::AddHandler(this, - TEXT("Test the tokenizer by tokenizing a string and printing the result.")); - } - virtual void Handle() override - { - WingTokenizer T(Input); - T.PrintEverything(WingOut::StdoutBuffer); - } -}; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_TypeToText.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Test_TypeToText.h deleted file mode 100644 index 5f638576..00000000 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_TypeToText.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingTypes.h" -#include "Test_TypeToText.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_Test_TypeToText : public UWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, meta=(Description="The type name to parse, e.g. 'Array'")) - FString Input; - - virtual void Register() override - { - UWingServer::AddHandler(this, - TEXT("Test the type parser by parsing a type name and dumping the resulting FEdGraphPinType.")); - } - virtual void Handle() override - { - FEdGraphPinType PinType; - UWingTypes::Requirements Require; - Require.BlueprintType = false; - Require.Blueprintable = false; - Require.AllowContainer = true; - - bool OK = UWingTypes::TextToType(Input, PinType, Require, WingOut::Stdout); - - auto& Out = WingOut::Stdout; - Out.Printf(TEXT("ParseResult: %s\n"), OK ? TEXT("OK") : TEXT("FAILED")); - Out.Printf(TEXT("PinCategory: %s\n"), *PinType.PinCategory.ToString()); - Out.Printf(TEXT("PinSubCategory: %s\n"), *PinType.PinSubCategory.ToString()); - Out.Printf(TEXT("PinSubCategoryObject: %s\n"), - PinType.PinSubCategoryObject.IsValid() - ? *PinType.PinSubCategoryObject->GetPathName() - : TEXT("(none)")); - Out.Printf(TEXT("ContainerType: %d\n"), (int32)PinType.ContainerType); - if (PinType.IsMap()) - { - Out.Printf(TEXT("ValueTerminalCategory: %s\n"), *PinType.PinValueType.TerminalCategory.ToString()); - Out.Printf(TEXT("ValueTerminalSubCategory: %s\n"), *PinType.PinValueType.TerminalSubCategory.ToString()); - Out.Printf(TEXT("ValueTerminalSubCategoryObject: %s\n"), - PinType.PinValueType.TerminalSubCategoryObject.IsValid() - ? *PinType.PinValueType.TerminalSubCategoryObject->GetPathName() - : TEXT("(none)")); - } - - if (OK) - { - FString RoundTrip = UWingTypes::TypeToText(PinType); - Out.Printf(TEXT("TypeToText: %s\n"), RoundTrip.IsEmpty() ? TEXT("(empty)") : *RoundTrip); - } - } -}; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Unsanitize.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Unsanitize.h deleted file mode 100644 index 15eff997..00000000 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_Unsanitize.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingTokenizer.h" -#include "Test_Unsanitize.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_Test_Unsanitize : public UWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, meta=(Description="The sanitized identifier to unsanitize")) - FString Input; - - virtual void Register() override - { - UWingServer::AddHandler(this, - TEXT("Test the unsanitizer by unsanitizing a string and printing the result.")); - } - virtual void Handle() override - { - FString Error; - FName Result = WingTokenizer::TryInternalizeID(Input, Error); - if (!Error.IsEmpty()) - { - WingOut::Stdout.Printf(TEXT("Error: %s\n"), *Error); - } - if (!Result.IsNone()) - { - WingOut::Stdout.Printf(TEXT("Result: %s\n"), *Result.ToString()); - } - } -}; diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingMaterialParameter.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingMaterialParameter.cpp index cc7cccd0..6876c365 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingMaterialParameter.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingMaterialParameter.cpp @@ -1,12 +1,13 @@ #include "WingMaterialParameter.h" #include "WingUtils.h" +#include "WingTokenizer.h" #include "WingServer.h" -TMap WingMaterialParameter::GetMaterialParameters(UMaterialInterface* Material) +WingMaterialParameter::InfoMetaMap WingMaterialParameter::GetMaterialParameters(UMaterialInterface* Material) { - TMap Result; + InfoMetaMap Result; if (!Material) return Result; - TMap Temp; + InfoMetaMap Temp; for (int32 i = 0; i < (int32)EMaterialParameterType::NumRuntime; i++) { Material->GetAllParametersOfType((EMaterialParameterType)i, Temp); @@ -31,48 +32,183 @@ bool WingMaterialParameter::ParseMaterialParameterAssociation(const FString& Str return true; } -void WingMaterialParameter::FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta) +void WingMaterialParameter::ReportBadToken(WingTokenizer &Tok, const TCHAR *Expected, WingOut Errors) { - // 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); + FString Whole = Tok.GetRange(FName(), 1000); + Tok.SaveCursor(FName()); + FString Next = Tok.GetRange(FName(), 1); + Errors.Printf(TEXT("Parsing %s, near %s: expected %s"), *Whole, *Next, Expected); +} - switch (Meta.Value.Type) +FString WingMaterialParameter::StringID(const FMaterialParameterInfo &ID) +{ + FString Ext = WingUtils::ExternalizeID(ID.Name); + if (ID.Association != EMaterialParameterAssociation::GlobalParameter) { - case EMaterialParameterType::Scalar: - WingOut::Stdout.Printf(TEXT(" %sScalar \"%s\" = %g\n"), *Prefix, *Info.Name.ToString(), Meta.Value.AsScalar()); - break; - case EMaterialParameterType::Vector: - { - FLinearColor C = Meta.Value.AsLinearColor(); - WingOut::Stdout.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; + const TCHAR *Prefix = TEXT("Layer"); + if (ID.Association == EMaterialParameterAssociation::BlendParameter) + Prefix = TEXT("Blend"); + return FString::Printf(TEXT("%s:%d:%s"), Prefix, ID.Index, *Ext); } - case EMaterialParameterType::DoubleVector: + else { - FVector4d V = Meta.Value.AsVector4d(); - WingOut::Stdout.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(Meta.Value.AsTextureObject()); - WingOut::Stdout.Printf(TEXT(" %sTexture \"%s\" = %s\n"), - *Prefix, *Info.Name.ToString(), Tex ? *WingUtils::FormatName(Tex) : TEXT("None")); - break; - } - case EMaterialParameterType::StaticSwitch: - WingOut::Stdout.Printf(TEXT(" %sStaticSwitch \"%s\" = %s\n"), - *Prefix, *Info.Name.ToString(), Meta.Value.AsStaticSwitch() ? TEXT("true") : TEXT("false")); - break; - default: - WingOut::Stdout.Printf(TEXT(" %sType%d \"%s\"\n"), *Prefix, (int)Meta.Value.Type, *Info.Name.ToString()); - break; + return Ext; } } +bool WingMaterialParameter::ParseID(const FString& IDString, FMaterialParameterInfo& ID, WingOut Errors) +{ + WingTokenizer Tok(IDString); + + ID.Association = EMaterialParameterAssociation::GlobalParameter; + ID.Index = 0; + ID.Name = FName(); + + // Parse the Layer or blend prefix. + if (Tok.TokenIs(TEXT("Layer")) || Tok.TokenIs(TEXT("Blend"))) + { + if (Tok.NextName() == TEXT("Layer")) + ID.Association = EMaterialParameterAssociation::LayerParameter; + else + ID.Association = EMaterialParameterAssociation::BlendParameter; + Tok.Advance(); + + if (!Tok.TokenIs(':')) + { ReportBadToken(Tok, TEXT("colon"), Errors); return false; } + Tok.Advance(); + + if ((!Tok.TokenIs(Tok.Identifier)) || + (!LexTryParseString(ID.Index, *Tok.NextName().ToString()))) + { ReportBadToken(Tok, TEXT("integer"), Errors); return false; } + Tok.Advance(); + + if ((ID.Index < 0)||(ID.Index > 50)) + { + Errors.Printf(TEXT("Layer/Blend index outside of reasonable range\n")); + return false; + } + + if (!Tok.TokenIs(':')) + { ReportBadToken(Tok, TEXT("colon"), Errors); return false; } + Tok.Advance(); + } + + // Parse the parameter name + if (!Tok.TokenIs(Tok.Identifier)) + { ReportBadToken(Tok, TEXT("parameter name"), Errors); return false; } + ID.Name = Tok.NextName(); + Tok.Advance(); + + if (!Tok.TokenIs(0)) + { ReportBadToken(Tok, TEXT("end of input"), Errors); return false; } + return true; +} + +FMaterialParameterMetadata *WingMaterialParameter::FindParameter( + InfoMetaMap &Map, const FMaterialParameterInfo &ID, WingOut Errors) +{ + FMaterialParameterMetadata* Found = Map.Find(ID); + if (!Found) + { + Errors.Printf(TEXT("No parameter named '%s' in this material"), *StringID(ID)); + return nullptr; + } + return Found; +} + +bool WingMaterialParameter::CheckNoCustomPrimitiveData( + const FMaterialParameterInfo &ID, const FMaterialParameterMetadata &Data, WingOut Errors) +{ + if (Data.PrimitiveDataIndex != INDEX_NONE) + { + Errors.Printf( + TEXT("Parameter '%s' uses custom primitive data, which cannot be set on a material instance"), + *StringID(ID)); + return false; + } + return true; +} + +FString WingMaterialParameter::FormatValue(const FMaterialParameterValue& Value) +{ + switch (Value.Type) + { + case EMaterialParameterType::Scalar: { + return FString::Printf(TEXT("scalar(%g)"), Value.AsScalar()); + } + case EMaterialParameterType::Vector: { + return FString::Printf(TEXT("vector(%s)"), *Value.AsLinearColor().ToString()); + } + case EMaterialParameterType::DoubleVector: { + return FString::Printf(TEXT("doublevector(%s)"), *Value.AsLinearColor().ToString()); + } + case EMaterialParameterType::Texture: { + UTexture* Tex = Cast(Value.AsTextureObject()); + return FString::Printf(TEXT("texture(%s)"), *Tex->GetPathName()); + } + case EMaterialParameterType::StaticSwitch: { + const TCHAR *Str = Value.AsStaticSwitch() ? TEXT("true") : TEXT("false"); + return FString::Printf(TEXT("staticswitch(%s)"), Str); + } + default: { + return FString::Printf(TEXT("unknown(%d)"), (int)Value.Type); + } + } +} + +bool WingMaterialParameter::RemoveOverride( + const FMaterialParameterInfo &ID, UMaterialInstanceConstant *MI, WingOut Errors) +{ + // Remove the override from all parameter arrays. + auto RemoveFrom = [&](auto& Arr) { + return Arr.RemoveAll([&](auto& Entry) { return Entry.ParameterInfo == ID; }); + }; + + 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); + + if (Removed == 0) + { + Errors.Printf(TEXT("No override found for parameter '%s'"), *StringID(ID)); + return false; + } + return true; +} + + +void WingMaterialParameter::Print( + const FMaterialParameterInfo &Info, const FMaterialParameterMetadata &Meta) +{ + WingOut::Stdout.Printf(TEXT(" %s %s\n"), + *WingMaterialParameter::StringID(Info), *WingMaterialParameter::FormatValue(Meta.Value)); +} + +void WingMaterialParameter::PrintAll(const InfoMetaMap &Params, bool Headings) +{ + // Overridden parameters first. + bool bHasOverrides = false; + for (auto& [Info, Meta] : Params) + { + if (!Meta.bOverride) continue; + if (Headings && !bHasOverrides) + { WingOut::Stdout.Print(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; } + WingMaterialParameter::Print(Info, Meta); + } + + // Inherited (non-overridden) parameters. + bool bHasInherited = false; + for (auto& [Info, Meta] : Params) + { + if (Meta.bOverride) continue; + if (Headings && !bHasInherited) + { WingOut::Stdout.Print(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; } + WingMaterialParameter::Print(Info, Meta); + } +} diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingTokenizer.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingTokenizer.cpp index 1b09bfb0..fd37de9c 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingTokenizer.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingTokenizer.cpp @@ -249,16 +249,16 @@ void WingTokenizer::SaveCursor(FName Name) SavedCursor.Emplace(Name, Cursor); } -FStringView WingTokenizer::GetRange(FName SavePos, int Extra) const +FString WingTokenizer::GetRange(FName SavePos, int Extra) const { int Lo = 0; for (auto &Pair : SavedCursor) if (Pair.Key == SavePos) Lo = Pair.Value; int Hi = (Next - Tokens.GetData()) + Extra; Hi = FMath::Clamp(Hi, Lo, Tokens.Num()); - if (Lo >= Hi) return FStringView(); + if (Lo >= Hi) return FString(); const TCHAR* Start = Tokens[Lo].Source.GetData(); const TCHAR* End = Tokens[Hi - 1].Source.GetData() + Tokens[Hi - 1].Source.Len(); - return FStringView(Start, End - Start); + return FString(End - Start, Start); } void WingTokenizer::PrintEverything(FStringBuilderBase &Out) const diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingMaterialParameter.h b/Plugins/UEWingman/Source/UEWingman/Public/WingMaterialParameter.h index 82e7287b..7fb38fd3 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingMaterialParameter.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingMaterialParameter.h @@ -2,13 +2,63 @@ #include "CoreMinimal.h" #include "Materials/Material.h" +#include "WingTokenizer.h" +#include "Materials/MaterialInstanceConstant.h" #include "WingHandler.h" class WingMaterialParameter { public: - static TMap GetMaterialParameters(UMaterialInterface* Material); + using InfoMetaMap = TMap; + + // Get all the material parameters of the specified material including + // both parameters that have been overridden and those that haven't. + // + static InfoMetaMap GetMaterialParameters(UMaterialInterface* Material); + + // Find a material parameter in the InfoMap. + // If it doesn't exist, or report an error and return nullptr. + // + static FMaterialParameterMetadata *FindParameter( + InfoMetaMap &Map, const FMaterialParameterInfo &ID, WingOut Errors); + + // If the material uses custom primitive data, print an error + // and return false. + // + static bool CheckNoCustomPrimitiveData( + const FMaterialParameterInfo &ID, const FMaterialParameterMetadata &Data, WingOut Errors); + + // Convert a parameter ID into an ID string. + // + static bool ParseID(const FString& Str, FMaterialParameterInfo& ID, WingOut Errors); + + // Parse a parameter ID String into a parameter ID. + // + static FString StringID(const FMaterialParameterInfo& ID); + + // Remove a material parameter override from a material instance. + // If there is no override, print an error and return false. + // + static bool RemoveOverride( + const FMaterialParameterInfo &ID, UMaterialInstanceConstant *MI, WingOut Errors); + + // Format a material parameter value. + // + static FString FormatValue(const FMaterialParameterValue& Value); + + // Print out a material parameter with ID. + // + static void Print(const FMaterialParameterInfo &Info, const FMaterialParameterMetadata &Meta); + + // Print out a map full of material parameters. If Headings is + // true, the parameters are grouped by inherited/overridden. + // + static void PrintAll(const InfoMetaMap &Params, bool Headings); + static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation, WingOut Errors); - static void FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta); + + +private: + static void ReportBadToken(WingTokenizer &Tok, const TCHAR *Expected, WingOut Errors); }; diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingTokenizer.h b/Plugins/UEWingman/Source/UEWingman/Public/WingTokenizer.h index 326c5590..0b345eb7 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingTokenizer.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingTokenizer.h @@ -1,6 +1,6 @@ #pragma once - #include "CoreMinimal.h" +#include "WingHandler.h" // ----------------------------------------------------------------- // @@ -130,7 +130,7 @@ struct WingTokenizer // Get the input text from a saved cursor position to the current // cursor, optionally including additional tokens. - FStringView GetRange(FName SavePos, int32 Extra = 0) const; + FString GetRange(FName SavePos, int32 Extra = 0) const; // Tokenize a line of input. The tokens are stored in // the token array, and the cursor is positioned on the first