diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_ClearParameter.h b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_ClearParameter.h index f7090b13..45bcab0e 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_ClearParameter.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_ClearParameter.h @@ -5,7 +5,7 @@ #include "WingHandler.h" #include "WingFetcher.h" #include "WingUtils.h" -#include "WingMaterialParameter.h" +#include "WingParameterEditor.h" #include "Materials/MaterialInstanceConstant.h" #include "MaterialTypes.h" #include "MaterialInstance_ClearParameter.generated.h" @@ -40,11 +40,11 @@ public: if (!MI) return; // Parse the parameter ID. - WingMaterialParameter::Info ID; - if (!WingMaterialParameter::ParseID(Parameter, ID, WingOut::Stdout)) + FWingParameterEditor::Info ID; + if (!FWingParameterEditor::ParseID(Parameter, ID, WingOut::Stdout)) return; - if (!WingMaterialParameter::RemoveOverride(ID, MI, WingOut::Stdout)) return + if (!FWingParameterEditor::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 29ec28c0..bfe32698 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_DumpParameters.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_DumpParameters.h @@ -5,7 +5,7 @@ #include "WingHandler.h" #include "WingFetcher.h" #include "WingUtils.h" -#include "WingMaterialParameter.h" +#include "WingParameterEditor.h" #include "Materials/MaterialInstanceConstant.h" #include "MaterialTypes.h" #include "MaterialInstance_DumpParameters.generated.h" @@ -35,8 +35,8 @@ public: UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast(); if (!MI) return; - auto AllParams = WingMaterialParameter::GetMaterialParameters(MI); - WingMaterialParameter::PrintAll(AllParams, true); + auto AllParams = FWingParameterEditor::GetMaterialParameters(MI); + FWingParameterEditor::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 deeb5bb4..9d5e1edb 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_SetParameter.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/MaterialInstance_SetParameter.h @@ -5,7 +5,7 @@ #include "WingHandler.h" #include "WingFetcher.h" #include "WingUtils.h" -#include "WingMaterialParameter.h" +#include "WingParameterEditor.h" #include "Materials/MaterialInstanceConstant.h" #include "MaterialTypes.h" #include "MaterialInstance_SetParameter.generated.h" @@ -42,19 +42,12 @@ public: if (!MI) return; // Parse the parameter ID. - WingMaterialParameter::Info ID; - if (!WingMaterialParameter::ParseID(Parameter, ID, WingOut::Stdout)) + FWingParameterEditor::Info ID; + if (!FWingParameterEditor::ParseID(Parameter, ID, WingOut::Stdout)) return; - // Make sure the parameter actually exists. - auto AllParams = WingMaterialParameter::GetMaterialParameters(MI); - FMaterialParameterMetadata* Meta = - WingMaterialParameter::FindParameter(AllParams, ID, WingOut::Stdout); - if (!Meta) return; - if (!WingMaterialParameter::CheckNoCustomPrimitiveData(ID, *Meta, WingOut::Stdout)) return; - // Set the parameter - if (!WingMaterialParameter::AddOverride(ID, MI, Value, WingOut::Stdout)) return; + if (!FWingParameterEditor::AddOverride(ID, MI, Value, WingOut::Stdout)) return; 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 2e9468ea..ebe69e9b 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Material_DumpParameters.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Material_DumpParameters.h @@ -5,7 +5,7 @@ #include "WingFetcher.h" #include "WingServer.h" #include "WingUtils.h" -#include "WingMaterialParameter.h" +#include "WingParameterEditor.h" #include "MaterialTypes.h" #include "Material_DumpParameters.generated.h" @@ -34,8 +34,8 @@ public: UMaterial* Mat = F.Asset(Material).Cast(); if (!Mat) return; - auto AllParams = WingMaterialParameter::GetMaterialParameters(Mat); - WingMaterialParameter::PrintAll(AllParams, false); + auto AllParams = FWingParameterEditor::GetMaterialParameters(Mat); + FWingParameterEditor::PrintAll(AllParams, false); if (AllParams.IsEmpty()) WingOut::Stdout.Printf(TEXT("No material parameters.\n")); } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingMaterialParameter.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingMaterialParameter.cpp deleted file mode 100644 index 3e09afa6..00000000 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingMaterialParameter.cpp +++ /dev/null @@ -1,335 +0,0 @@ -#include "WingMaterialParameter.h" -#include "WingUtils.h" -#include "WingTokenizer.h" -#include "WingServer.h" -#include "Engine/Font.h" -#include "Engine/TextureCollection.h" -#include "Misc/DefaultValueHelper.h" -#include "SparseVolumeTexture/SparseVolumeTexture.h" -#include "VT/RuntimeVirtualTexture.h" - -const TCHAR *WingMaterialParameter::EMaterialParameterTypeToString(EMaterialParameterType Type) -{ - switch (Type) - { - case EMaterialParameterType::Scalar: return TEXT("Scalar"); - case EMaterialParameterType::Vector: return TEXT("Vector"); - case EMaterialParameterType::DoubleVector: return TEXT("DoubleVector"); - case EMaterialParameterType::Texture: return TEXT("Texture"); - case EMaterialParameterType::TextureCollection: return TEXT("TextureCollection"); - case EMaterialParameterType::Font: return TEXT("Font"); - case EMaterialParameterType::RuntimeVirtualTexture: return TEXT("RuntimeVirtualTexture"); - case EMaterialParameterType::SparseVolumeTexture: return TEXT("SparseVolumeTexture"); - case EMaterialParameterType::StaticSwitch: return TEXT("StaticSwitch"); - case EMaterialParameterType::StaticComponentMask: return TEXT("StaticComponentMask"); - case EMaterialParameterType::Num: return TEXT("Num"); - case EMaterialParameterType::None: return TEXT("None"); - } - return TEXT("None"); -} - -bool WingMaterialParameter::EMaterialParameterTypeFromName(FName Name, EMaterialParameterType &Result, WingOut Errors) -{ - if (Name == TEXT("Scalar")) { Result = EMaterialParameterType::Scalar; return true; } - if (Name == TEXT("Vector")) { Result = EMaterialParameterType::Vector; return true; } - if (Name == TEXT("DoubleVector")) { Result = EMaterialParameterType::DoubleVector; return true; } - if (Name == TEXT("Texture")) { Result = EMaterialParameterType::Texture; return true; } - if (Name == TEXT("TextureCollection")) { Result = EMaterialParameterType::TextureCollection; return true; } - if (Name == TEXT("Font")) { Result = EMaterialParameterType::Font; return true; } - if (Name == TEXT("RuntimeVirtualTexture")) { Result = EMaterialParameterType::RuntimeVirtualTexture; return true; } - if (Name == TEXT("SparseVolumeTexture")) { Result = EMaterialParameterType::SparseVolumeTexture; return true; } - if (Name == TEXT("StaticSwitch")) { Result = EMaterialParameterType::StaticSwitch; return true; } - if (Name == TEXT("StaticComponentMask")) { Result = EMaterialParameterType::StaticComponentMask; return true; } - if (Name == TEXT("Num")) { Result = EMaterialParameterType::Num; return true; } - if (Name == TEXT("None")) { Result = EMaterialParameterType::None; return true; } - Errors.Printf(TEXT("ERROR: '%s' is not a valid EMaterialParameterType\n"), *Name.ToString()); - Result = EMaterialParameterType::None; - return false; -} - -WingMaterialParameter::InfoMetaMap WingMaterialParameter::GetMaterialParameters(UMaterialInterface* Material) -{ - InfoMetaMap Result; - if (!Material) return Result; - TMap Temp; - for (int I = 0; I < int(EMaterialParameterType::NumRuntime); I++) - { - EMaterialParameterType T = (EMaterialParameterType)I; - Material->GetAllParametersOfType(T, Temp); - for (const auto &KV : Temp) - { - check(KV.Value.Value.Type == T); - Result.Add(Info(KV.Key, T), KV.Value); - } - } - return Result; -} - -void WingMaterialParameter::ReportBadToken(WingTokenizer &Tok, const TCHAR *Expected, WingOut Errors) -{ - 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); -} - -FString WingMaterialParameter::StringID(const Info &ID) -{ - const TCHAR *Type = EMaterialParameterTypeToString(ID.Type); - FString Ext = WingUtils::ExternalizeID(ID.Name); - if (ID.Association != EMaterialParameterAssociation::GlobalParameter) - { - const TCHAR *Prefix = TEXT("Layer"); - if (ID.Association == EMaterialParameterAssociation::BlendParameter) - Prefix = TEXT("Blend"); - return FString::Printf(TEXT("%s:%s:%d:%s"), Type, Prefix, ID.Index, *Ext); - } - else - { - return FString::Printf(TEXT("%s:%s"), Type, *Ext); - } -} - -bool WingMaterialParameter::ParseID(const FString& IDString, Info& ID, WingOut Errors) -{ - WingTokenizer Tok(IDString); - - ID.Type = EMaterialParameterType::None; - ID.Association = EMaterialParameterAssociation::GlobalParameter; - ID.Index = 0; - ID.Name = FName(); - - // Parse the type. - const TCHAR *GoodTypes = TEXT("Vector, DoubleVector, Texture, TextureCollection, Font, RuntimeVirtualTexture, SparseVolumeTexture, or StaticSwitch"); - if (!Tok.TokenIs(Tok.Identifier)) - { ReportBadToken(Tok, GoodTypes, Errors); return false; } - EMaterialParameterType TypeValue; - if (!EMaterialParameterTypeFromName(Tok.NextName(), TypeValue, Errors)) return false; - if (TypeValue >= EMaterialParameterType::NumRuntime) - { ReportBadToken(Tok, GoodTypes, Errors); return false; } - ID.Type = TypeValue; - Tok.Advance(); - - // A colon after the type. - if (!Tok.TokenIs(':')) { ReportBadToken(Tok, TEXT("colon"), Errors); return false; } - - // 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(); - 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; -} - -WingMaterialParameter::Metadata *WingMaterialParameter::FindParameter( - InfoMetaMap &Map, const Info &ID, WingOut Errors) -{ - Metadata* Found = Map.Find(ID); - if (!Found) - { - Errors.Printf(TEXT("No parameter '%s' in this material"), *StringID(ID)); - return nullptr; - } - return Found; -} - -bool WingMaterialParameter::CheckNoCustomPrimitiveData( - const Info &ID, const Metadata &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("%g"), Value.AsScalar()); - } - case EMaterialParameterType::Vector: { - return Value.AsLinearColor().ToString(); - } - case EMaterialParameterType::DoubleVector: { - return Value.AsVector4d().ToString(); - } - case EMaterialParameterType::Texture: { - return PathNameOrNone(Value.Texture); - } - case EMaterialParameterType::TextureCollection: { - return PathNameOrNone(Value.TextureCollection); - } - case EMaterialParameterType::Font: { - return FString::Printf(TEXT("%s:%d"), *PathNameOrNone(Value.Font.Value), Value.Font.Page); - } - case EMaterialParameterType::RuntimeVirtualTexture: { - return PathNameOrNone(Value.RuntimeVirtualTexture); - } - case EMaterialParameterType::SparseVolumeTexture: { - return PathNameOrNone(Value.SparseVolumeTexture); - } - case EMaterialParameterType::StaticSwitch: { - return Value.AsStaticSwitch() ? TEXT("true") : TEXT("false"); - } - case EMaterialParameterType::StaticComponentMask: { - const FStaticComponentMaskValue Mask = Value.AsStaticComponentMask(); - return FString::Printf( - TEXT("(R=%s,G=%s,B=%s,A=%s)"), - Mask.R ? TEXT("true") : TEXT("false"), - Mask.G ? TEXT("true") : TEXT("false"), - Mask.B ? TEXT("true") : TEXT("false"), - Mask.A ? TEXT("true") : TEXT("false")); - } - case EMaterialParameterType::Num: { - return TEXT("invalid-parameter-type"); - } - case EMaterialParameterType::None: { - return TEXT("invalid-parameter-type"); - } - } - return TEXT("invalid-parameter-type"); -} - -bool WingMaterialParameter::AddOverride( - const Info &ID, UMaterialInstanceConstant *MI, const FString &Value, WingOut Errors) -{ - switch (ID.Type) - { - case EMaterialParameterType::Scalar: - { - float ScalarValue; - if (!FDefaultValueHelper::ParseFloat(Value, ScalarValue)) - { - Errors.Printf(TEXT("Failed to parse '%s' as a float"), *Value); - return false; - } - MI->SetScalarParameterValueEditorOnly(ID, ScalarValue); - return true; - } - case EMaterialParameterType::Vector: - { - FLinearColor Color; - if (!Color.InitFromString(Value)) - { - Errors.Printf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value); - return false; - } - MI->SetVectorParameterValueEditorOnly(ID, Color); - return true; - } - case EMaterialParameterType::DoubleVector: - case EMaterialParameterType::Texture: - case EMaterialParameterType::TextureCollection: - case EMaterialParameterType::Font: - case EMaterialParameterType::RuntimeVirtualTexture: - case EMaterialParameterType::SparseVolumeTexture: - case EMaterialParameterType::StaticSwitch: - case EMaterialParameterType::StaticComponentMask: - Errors.Printf(TEXT("Parameters of type %s are not implemented"), EMaterialParameterTypeToString(ID.Type)); - return false; - case EMaterialParameterType::Num: - case EMaterialParameterType::None: - Errors.Printf(TEXT("Parameter Type is not valid")); - return false; - } - return false; -} - -bool WingMaterialParameter::RemoveOverride( - const Info &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; - switch (ID.Type) - { - case EMaterialParameterType::Scalar: Removed += RemoveFrom(MI->ScalarParameterValues); break; - case EMaterialParameterType::Vector: Removed += RemoveFrom(MI->VectorParameterValues); break; - case EMaterialParameterType::DoubleVector: Removed += RemoveFrom(MI->DoubleVectorParameterValues); break; - case EMaterialParameterType::Texture: Removed += RemoveFrom(MI->TextureParameterValues); break; - case EMaterialParameterType::TextureCollection: Removed += RemoveFrom(MI->TextureCollectionParameterValues); break; - case EMaterialParameterType::Font: Removed += RemoveFrom(MI->FontParameterValues); break; - case EMaterialParameterType::RuntimeVirtualTexture: Removed += RemoveFrom(MI->RuntimeVirtualTextureParameterValues); break; - case EMaterialParameterType::SparseVolumeTexture: Removed += RemoveFrom(MI->SparseVolumeTextureParameterValues); break; - case EMaterialParameterType::StaticSwitch: Errors.Printf(TEXT("Removal of static switches not implemented")); return false; - case EMaterialParameterType::StaticComponentMask: Errors.Printf(TEXT("Removal of static component masks not implemented")); return false; - case EMaterialParameterType::Num: Errors.Printf(TEXT("Parameter Type is not valid")); return false; - case EMaterialParameterType::None: Errors.Printf(TEXT("Parameter Type is not valid")); return false; - } - - if (Removed == 0) - { - Errors.Printf(TEXT("No override found for parameter '%s'"), *StringID(ID)); - return false; - } - return true; -} - - -void WingMaterialParameter::Print( - const Info &Info, const Metadata &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/WingParameterEditor.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingParameterEditor.cpp new file mode 100644 index 00000000..b1c8b2ba --- /dev/null +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingParameterEditor.cpp @@ -0,0 +1,347 @@ +#include "WingParameterEditor.h" +#include "WingUtils.h" +#include "WingTokenizer.h" +#include "WingProperty.h" +#include "WingServer.h" +#include "Engine/Font.h" +#include "Engine/TextureCollection.h" +#include "Misc/DefaultValueHelper.h" +#include "SparseVolumeTexture/SparseVolumeTexture.h" +#include "VT/RuntimeVirtualTexture.h" + +FWingParameterEditor::GetterAndSetter::GetterAndSetter(FName N, GetterFunc G, SetterFunc S) +{ + Property = FWingParameterEditor::StaticStruct()->FindPropertyByName(N); + Getter = G; + Setter = S; +} + +FWingParameterEditor::GetterAndSetterMap FWingParameterEditor::CalcGetterAndSetterMap() +{ + using Editor = FWingParameterEditor; + using Value = FMaterialParameterValue; + using Type = EMaterialParameterType; + + GetterAndSetterMap M; + + M.Add(Type::Scalar, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, Scalar), + [](Editor &E)->Value { return Value(E.Scalar); }, + [](Editor &E, const Value &V) { E.Scalar = V.AsScalar(); })); + + M.Add(Type::Vector, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, Vector), + [](Editor &E)->Value { return Value(E.Vector); }, + [](Editor &E, const Value &V) { E.Vector = V.AsLinearColor(); })); + + M.Add(Type::DoubleVector, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, DoubleVector), + [](Editor &E)->Value { return Value(E.DoubleVector); }, + [](Editor &E, const Value &V) { E.DoubleVector = V.AsVector4d(); })); + + M.Add(Type::Texture, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, Texture), + [](Editor &E)->Value { return Value(E.Texture); }, + [](Editor &E, const Value &V) { E.Texture = V.Texture; })); + + M.Add(Type::TextureCollection, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, TextureCollection), + [](Editor &E)->Value { return Value(E.TextureCollection); }, + [](Editor &E, const Value &V) { E.TextureCollection = V.TextureCollection; })); + + M.Add(Type::Font, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, Font), + [](Editor &E)->Value { return Value(E.Font.Font, E.Font.Page); }, + [](Editor &E, const Value &V) { E.Font.Font = V.Font.Value; E.Font.Page = V.Font.Page; })); + + M.Add(Type::RuntimeVirtualTexture, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, RuntimeVirtualTexture), + [](Editor &E)->Value { return Value(E.RuntimeVirtualTexture); }, + [](Editor &E, const Value &V) { E.RuntimeVirtualTexture = V.RuntimeVirtualTexture; })); + + M.Add(Type::SparseVolumeTexture, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, SparseVolumeTexture), + [](Editor &E)->Value { return Value(E.SparseVolumeTexture); }, + [](Editor &E, const Value &V) { E.SparseVolumeTexture = V.SparseVolumeTexture; })); + + M.Add(Type::StaticSwitch, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, StaticSwitch), + [](Editor &E)->Value { return Value(E.StaticSwitch); }, + [](Editor &E, const Value &V) { E.StaticSwitch = V.AsStaticSwitch(); })); + + M.Add(Type::StaticComponentMask, GetterAndSetter( + GET_MEMBER_NAME_CHECKED(FWingParameterEditor, StaticComponentMask), + [](Editor &E)->Value { return Value(E.StaticComponentMask); }, + [](Editor &E, const Value &V) { E.StaticComponentMask = V.AsStaticComponentMask(); })); + + return M; +} + +const FWingParameterEditor::GetterAndSetterMap &FWingParameterEditor::GetGetterAndSetterMap() +{ + static GetterAndSetterMap TheMap = CalcGetterAndSetterMap(); + return TheMap; +} + +FName FWingParameterEditor::TypeToName(EMaterialParameterType Type) +{ + const GetterAndSetter *GS = GetGetterAndSetterMap().Find(Type); + if (GS == nullptr) return FName(TEXT("unknown")); + return GS->Property->GetFName(); +} + +bool FWingParameterEditor::NameToType(FName Name, EMaterialParameterType &Result, WingOut Errors) +{ + for (const auto &Pair : GetGetterAndSetterMap()) + { + if (Pair.Value.Property->GetName() == Name) + { + Result = Pair.Key; return true; + } + } + Errors.Printf(TEXT("ERROR: '%s' is not a valid EMaterialParameterType\n"), *Name.ToString()); + Result = EMaterialParameterType::None; + return false; +} + +FWingParameterEditor::InfoMetaMap FWingParameterEditor::GetMaterialParameters(UMaterialInterface* Material) +{ + InfoMetaMap Result; + if (!Material) return Result; + TMap Temp; + for (int I = 0; I < int(EMaterialParameterType::Num); I++) + { + EMaterialParameterType T = (EMaterialParameterType)I; + Material->GetAllParametersOfType(T, Temp); + for (const auto &KV : Temp) + { + check(KV.Value.Value.Type == T); + Result.Add(Info(KV.Key, T), KV.Value); + } + } + return Result; +} + +bool FWingParameterEditor::ReportBadType(EMaterialParameterType Type, WingOut Errors) +{ + Errors.Printf(TEXT("Material parameter type #%d is not a valid type.\n")); + return false; +} + +void FWingParameterEditor::ReportBadToken(WingTokenizer &Tok, const TCHAR *Expected, WingOut Errors) +{ + 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); +} + +FString FWingParameterEditor::StringID(const Info &ID) +{ + FString TypeName = TypeToName(ID.Type).ToString(); + FString Ext = WingUtils::ExternalizeID(ID.Name); + if (ID.Association != EMaterialParameterAssociation::GlobalParameter) + { + const TCHAR *Prefix = TEXT("Layer"); + if (ID.Association == EMaterialParameterAssociation::BlendParameter) + Prefix = TEXT("Blend"); + return FString::Printf(TEXT("%s:%s:%d:%s"), *TypeName, Prefix, ID.Index, *Ext); + } + else + { + return FString::Printf(TEXT("%s:%s"), *TypeName, *Ext); + } +} + +bool FWingParameterEditor::ParseID(const FString& IDString, Info& ID, WingOut Errors) +{ + WingTokenizer Tok(IDString); + + ID.Type = EMaterialParameterType::None; + ID.Association = EMaterialParameterAssociation::GlobalParameter; + ID.Index = INDEX_NONE; + ID.Name = FName(); + + // Parse the type. + const TCHAR *GoodTypes = TEXT("Scalar, Vector, DoubleVector, Texture, TextureCollection, Font, " + "RuntimeVirtualTexture, SparseVolumeTexture, StaticSwitch, or StaticComponentMask"); + if (!Tok.TokenIs(Tok.Identifier)) + { ReportBadToken(Tok, GoodTypes, Errors); return false; } + EMaterialParameterType TypeValue; + if (!NameToType(Tok.NextName(), TypeValue, Errors)) return false; + if (TypeValue >= EMaterialParameterType::Num) + { ReportBadToken(Tok, GoodTypes, Errors); return false; } + ID.Type = TypeValue; + Tok.Advance(); + + // A colon after the type. + if (!Tok.TokenIs(':')) { ReportBadToken(Tok, TEXT("colon"), Errors); return false; } + Tok.Advance(); + + // 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(); + 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; +} + +FWingParameterEditor::Metadata *FWingParameterEditor::FindParameter( + InfoMetaMap &Map, const Info &ID, WingOut Errors) +{ + Metadata* Found = Map.Find(ID); + if (!Found) + { + Errors.Printf(TEXT("No parameter '%s' in this material"), *StringID(ID)); + return nullptr; + } + return Found; +} + +bool FWingParameterEditor::CheckNoCustomPrimitiveData( + const Info &ID, const Metadata &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; +} + +bool FWingParameterEditor::AddOverride( + const Info &ID, UMaterialInstanceConstant *MI, const FString &StrVal, WingOut Errors) +{ + // Verify that the parameter type is valid. + const GetterAndSetter *GS = GetGetterAndSetterMap().Find(ID.Type); + if (GS == nullptr) return ReportBadType(ID.Type, Errors); + + // Find the existing parameter. + TMap Lookup; + MI->GetAllParametersOfType(ID.Type, Lookup); + FMaterialParameterMetadata *Meta = Lookup.Find(ID); + if (Meta == nullptr) + { + Errors.Printf(TEXT("No parameter named %s to override.\n"), *StringID(ID)); + return false; + } + check(Meta->Value.Type == ID.Type); + + // If the parameter involves the use of custom primitive data, we + // cannot meaningfully override it. + if (!CheckNoCustomPrimitiveData(ID, *Meta, Errors)) return false; + + // Parse the value string. + FWingParameterEditor Editor; + if (!FWingProperty(GS->Property, &Editor, true).SetText(StrVal, Errors)) return false; + Meta->Value = GS->Getter(Editor); + + // Apply the update. + FMaterialInstanceParameterUpdateContext Update(MI); + Update.SetParameterValueEditorOnly(ID, *Meta); + return true; +} + +bool FWingParameterEditor::RemoveOverride( + const Info &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; + switch (ID.Type) + { + case EMaterialParameterType::Scalar: Removed += RemoveFrom(MI->ScalarParameterValues); break; + case EMaterialParameterType::Vector: Removed += RemoveFrom(MI->VectorParameterValues); break; + case EMaterialParameterType::DoubleVector: Removed += RemoveFrom(MI->DoubleVectorParameterValues); break; + case EMaterialParameterType::Texture: Removed += RemoveFrom(MI->TextureParameterValues); break; + case EMaterialParameterType::TextureCollection: Removed += RemoveFrom(MI->TextureCollectionParameterValues); break; + case EMaterialParameterType::Font: Removed += RemoveFrom(MI->FontParameterValues); break; + case EMaterialParameterType::RuntimeVirtualTexture: Removed += RemoveFrom(MI->RuntimeVirtualTextureParameterValues); break; + case EMaterialParameterType::SparseVolumeTexture: Removed += RemoveFrom(MI->SparseVolumeTextureParameterValues); break; + case EMaterialParameterType::StaticSwitch: Errors.Printf(TEXT("Removal of static switches not implemented")); return false; + case EMaterialParameterType::StaticComponentMask: Errors.Printf(TEXT("Removal of static component masks not implemented")); return false; + case EMaterialParameterType::Num: Errors.Printf(TEXT("Parameter Type is not valid")); return false; + case EMaterialParameterType::None: Errors.Printf(TEXT("Parameter Type is not valid")); return false; + } + + if (Removed == 0) + { + Errors.Printf(TEXT("No override found for parameter '%s'"), *StringID(ID)); + return false; + } + return true; +} + + +void FWingParameterEditor::Print(const Info &ID, const Metadata &Meta) +{ + const GetterAndSetter *GS = GetGetterAndSetterMap().Find(ID.Type); + if ((GS == nullptr) || (ID.Type != Meta.Value.Type)) + { + WingOut::Stdout.Printf(TEXT(" corrupted parameter: %s\n"), + *StringID(ID)); + return; + } + FWingParameterEditor Editor; + GS->Setter(Editor, Meta.Value); + FString StrVal = FWingProperty(GS->Property, &Editor, false).GetText(); + WingOut::Stdout.Printf(TEXT(" %s %s\n"), + *StringID(ID), *StrVal); +} + +void FWingParameterEditor::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; } + FWingParameterEditor::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; } + FWingParameterEditor::Print(Info, Meta); + } +} diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingMaterialParameter.h b/Plugins/UEWingman/Source/UEWingman/Public/WingParameterEditor.h similarity index 56% rename from Plugins/UEWingman/Source/UEWingman/Public/WingMaterialParameter.h rename to Plugins/UEWingman/Source/UEWingman/Public/WingParameterEditor.h index 901c6883..b36ab632 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingMaterialParameter.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingParameterEditor.h @@ -5,11 +5,61 @@ #include "WingTokenizer.h" #include "Materials/MaterialInstanceConstant.h" #include "WingHandler.h" +#include "WingParameterEditor.generated.h" - - -class WingMaterialParameter +USTRUCT() +struct FWingFontAndPage { + GENERATED_BODY() +public: + UPROPERTY(EditAnywhere) + UFont *Font; + + UPROPERTY(EditAnywhere) + int32 Page; +}; + + +// FWingParameterEditor is a tool for modifying material +// parameters in material instances. Transferring values +// into UPROPERTY fields makes it possible to use GetText +// and SetText to parse them. +// +USTRUCT() +struct FWingParameterEditor +{ + GENERATED_BODY() +private: + UPROPERTY(EditAnywhere) + float Scalar; + + UPROPERTY(EditAnywhere) + FLinearColor Vector; + + UPROPERTY(EditAnywhere) + FVector4d DoubleVector; + + UPROPERTY(EditAnywhere) + UTexture *Texture; + + UPROPERTY(EditAnywhere) + UTextureCollection *TextureCollection; + + UPROPERTY(EditAnywhere) + FWingFontAndPage Font; + + UPROPERTY(EditAnywhere) + URuntimeVirtualTexture *RuntimeVirtualTexture; + + UPROPERTY(EditAnywhere) + USparseVolumeTexture *SparseVolumeTexture; + + UPROPERTY(EditAnywhere) + bool StaticSwitch; + + UPROPERTY(EditAnywhere) + FStaticComponentMaskValue StaticComponentMask; + public: // Info struct is a FMaterialParameterInfo plus a type field. struct Info : public FMaterialParameterInfo @@ -32,12 +82,17 @@ public: } }; - - using Type = EMaterialParameterType; using Metadata = FMaterialParameterMetadata; - using Value = FMaterialParameterValue; using InfoMetaMap = TMap; + // Convert a type to a name. + // + static FName TypeToName(EMaterialParameterType Type); + + // Convert a name to a type, if possible. + // + static bool NameToType(FName Name, EMaterialParameterType &Result, WingOut Errors); + // Get all the material parameters of the specified material including // both parameters that have been overridden and those that haven't. // @@ -48,7 +103,7 @@ public: // static Metadata *FindParameter(InfoMetaMap &Map, const Info &ID, WingOut Errors); - // If the material uses custom primitive data, print an error + // If the material uses custom primitive data, print an error // and return false. // static bool CheckNoCustomPrimitiveData(const Info &ID, const Metadata &Data, WingOut Errors); @@ -64,17 +119,13 @@ public: // Add a material parameter override to a material instance. // static bool AddOverride( - const Info &ID, UMaterialInstanceConstant *MI, const FString &Value, WingOut Errors); + const Info &ID, UMaterialInstanceConstant *MI, const FString &StrVal, WingOut Errors); // Remove a material parameter override from a material instance. // If there is no override, print an error and return false. // static bool RemoveOverride(const Info &ID, UMaterialInstanceConstant *MI, WingOut Errors); - // Format a material parameter value. - // - static FString FormatValue(const Value& Value); - // Print out a material parameter with ID. // static void Print(const Info &Info, const Metadata &Meta); @@ -84,10 +135,28 @@ public: // static void PrintAll(const InfoMetaMap &Params, bool Headings); +private: + using GetterFunc = TFunction; + using SetterFunc = TFunction; + + struct GetterAndSetter + { + FProperty *Property; + GetterFunc Getter; + SetterFunc Setter; + GetterAndSetter(FName N, GetterFunc G, SetterFunc S); + }; + + using GetterAndSetterMap = TMap; + + static void Add(GetterAndSetterMap &M, + EMaterialParameterType Type, FName Name, GetterFunc G, SetterFunc S); + + static GetterAndSetterMap CalcGetterAndSetterMap(); + static const GetterAndSetterMap &GetGetterAndSetterMap(); + private: static void ReportBadToken(WingTokenizer &Tok, const TCHAR *Expected, WingOut Errors); - static const TCHAR *EMaterialParameterTypeToString(EMaterialParameterType Type); - static bool EMaterialParameterTypeFromName(FName Name, EMaterialParameterType &Result, WingOut Errors); - static FString PathNameOrNone(UObject *Obj) { return Obj ? Obj->GetPathName() : FString(TEXT("None")); } + static bool ReportBadType(EMaterialParameterType Type, WingOut Errors); };