├── .gitattributes ├── .gitignore ├── BUIValidator.uplugin ├── Config └── FilterPlugin.ini ├── LICENSE ├── README.md └── Source └── BUIValidator ├── BUIValidator.Build.cs ├── Private ├── BUIEditorValidator_RequiredProperty.cpp ├── BUIEditorValidator_Textures.cpp ├── BUIValidateAllCommandlet.cpp ├── BUIValidateAllCommandlet.h ├── BUIValidatorModule.cpp └── BUIValidatorSettings.cpp └── Public ├── BUIEditorValidator_RequiredProperty.h ├── BUIEditorValidator_Textures.h ├── BUIValidatorModule.h └── BUIValidatorSettings.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Visual Studio 2015 user specific files 3 | .vs/ 4 | 5 | # Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Compiled Dynamic libraries 16 | *.so 17 | *.dylib 18 | *.dll 19 | 20 | # Fortran module files 21 | *.mod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | *.ipa 34 | 35 | # These project files can be generated by the engine 36 | *.xcodeproj 37 | *.xcworkspace 38 | *.sln 39 | *.suo 40 | *.opensdf 41 | *.sdf 42 | *.VC.db 43 | *.VC.opendb 44 | 45 | Binaries/* 46 | Build/* 47 | Intermediate/* 48 | 49 | -------------------------------------------------------------------------------- /BUIValidator.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.1", 5 | "FriendlyName": "BUIValidator", 6 | "Description": "Validate assets based on rules", 7 | "Category": "Other", 8 | "CreatedBy": "benui", 9 | "CreatedByURL": "https://benui.ca/", 10 | "DocsURL": "https://github.com/benui-dev/UE4-BUIValidator", 11 | "MarketplaceURL": "", 12 | "SupportURL": "https://github.com/benui-dev/UE4-BUIValidator", 13 | "CanContainContent": false, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "BUIValidator", 20 | "Type": "Editor", 21 | "LoadingPhase": "Default", 22 | "WhitelistPlatforms": [ "Win64", "Mac", "Linux" ] 23 | } 24 | ], 25 | "Plugins": [ 26 | { 27 | "Name": "DataValidation", 28 | "Enabled": true 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 4 | ; 5 | ; Examples: 6 | ; /README.txt 7 | ; /Extras/... 8 | ; /Binaries/ThirdParty/*.dll 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unreal Engine Texture Validator Plugin 2 | 3 | A plugin that both validates and automatically applies settings for texture assets in Unreal Engine. 4 | 5 | ## What can it do? 6 | 7 | * Throw errors when texture size, texture group and other properties do not 8 | match customizable rules. 9 | * Automatically set default values for new textures upon import. 10 | * Set different validation rules for textures with different prefixes, or for 11 | different folders within Unreal. 12 | * Throw errors when users import textures without setting the Data Source 13 | Folder in their Editor Settings. 14 | * Throw errors when `UPROPERTY()` values are not set. 15 | * Run all rules and validate assets from command-line 16 | 17 | ## Usage 18 | 19 | Once installed, open Project Settings > BUI Validator. 20 | 21 | Here you can set up validator groups, that consist of match conditions and 22 | rules to be applied to those matches. 23 | 24 | ![Settings example](https://benui.ca/assets/unreal/validator-settings-example.png) 25 | 26 | ### Settings 27 | 28 | #### Match Conditions 29 | 30 | For a rule to be triggered, **all** conditions of the match must pass. 31 | 32 | * **Texture groups:** Match textures with _any_ of the texture groups. 33 | * **Prefixes:** Match textures with _any_ of the asset name prefixes. 34 | * **Suffixes:** Match textures with _any_ of the asset name suffixes. 35 | * **Paths:** Match textures in _any_ of the asset directories. 36 | 37 | #### Validator Rules 38 | 39 | Each rule allows multiple values for a given setting. For example a texture 40 | could be allowed to be within World or UI. To pass, the asset must match 41 | **any** of the values. 42 | 43 | * **Texture groups:** Textures must have one of the specified Texture Groups. 44 | * **Compression Settings:** Textures must have one of the specified 45 | Compression Settings. 46 | * **Pixel Formats:** Textures must have one of these Pixel Formats 47 | * **Mip Gen Settings:** Textures must have one of these Mip Gen Settings 48 | * **Prefixes:** Textures must have one of these prefixes. 49 | * **Texture Size:** Textures must pass these size requirements. Size 50 | requirements are "Multiple of Four" or "Power of Two" 51 | * **Paths:** Textures must be within this path in Unreal. 52 | * **Require Data Source Folder:** Require that the Data Source Folder be set in 53 | Editor Preferences, and that assets are imported from there. This is to aid [reimporting between team members](https://benui.ca/unreal/reimporting-assets/). 54 | 55 | #### Auto-apply settings to new textures 56 | 57 | Validator Groups have an _Apply On Import_ option. When checked, any 58 | newly-imported assets that match the group will have some rules automatically 59 | applied to them. 60 | 61 | Prefix, Texture Size and Path rules are not applied on import. 62 | 63 | ### C++ UPROPERTY() Settings 64 | 65 | The plugin also supports making `UPROPERTY` properties _required_. 66 | 67 | ```cpp 68 | // If this is not set, the BP will show an error on save 69 | UPROPERTY( EditAnywhere, meta = ( BUIRequired = "true" ) ) 70 | TSubclassOf RequiredActor; 71 | ``` 72 | 73 | ![BUIRequired flow example](https://benui.ca/assets/unreal/buivalidator-buirequired.png) 74 | 75 | ### Running Validation 76 | 77 | Whenever UTexture2D asset is saved, the rules in Project Settings are applied. 78 | If the asset does not pass, an error is shown. 79 | 80 | ![Failure example](https://benui.ca/assets/unreal/validator-fail-example.png) 81 | 82 | ### Command-line Validation 83 | 84 | This is useful when validating assets as part of a build process. 85 | 86 | ``` 87 | "C:\Program Files\Epic Games\UE_5.0EA\Engine\Binaries\Win64\UnrealEditor-Win64-DebugGame-Cmd.exe" -run=BUIValidateAll 88 | ``` 89 | 90 | ## Installation 91 | 92 | 1. Download the zip or clone the Github repository into 93 | `YourProject/Plugins/BUIValidator/` 94 | 2. Add the following to the end of your `.uproject` file, inside the `{ }` 95 | ```json 96 | "Plugins": [ 97 | { 98 | "Name": "BUIValidator", 99 | "Enabled": true 100 | } 101 | ] 102 | ``` 103 | 104 | ## License 105 | 106 | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) 107 | 108 | ## Contact 109 | 110 | If you find it useful, drop me a line [@_benui](https://twitter.com/_benui) on Twitter. 111 | 112 | [benui.ca](https://benui.ca) 113 | 114 | 115 | ## Future Work 116 | 117 | * Add support for validating builds using the [Data Validation plugin](https://docs.unrealengine.com/5.0/en-US/data-validation/) 118 | * Regular expression support 119 | * More explicit support for And/Or matching 120 | * Support more texture settings (sRGB, others) 121 | * Support more data types 122 | 123 | -------------------------------------------------------------------------------- /Source/BUIValidator/BUIValidator.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class BUIValidator : ModuleRules 6 | { 7 | public BUIValidator(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] 12 | { 13 | "Core", 14 | }); 15 | 16 | 17 | PrivateDependencyModuleNames.AddRange(new string[] 18 | { 19 | "CoreUObject", 20 | "Engine", 21 | "DataValidation", 22 | "UnrealEd" 23 | }); 24 | } 25 | } -------------------------------------------------------------------------------- /Source/BUIValidator/Private/BUIEditorValidator_RequiredProperty.cpp: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #include "BUIEditorValidator_RequiredProperty.h" 4 | #include "Engine/Texture2D.h" 5 | #include "UObject/UnrealType.h" 6 | 7 | #define LOCTEXT_NAMESPACE "BUIEditorValidator" 8 | 9 | const FName UBUIEditorValidator_RequiredProperty::PropertyName = "BUIRequired"; 10 | 11 | UBUIEditorValidator_RequiredProperty::UBUIEditorValidator_RequiredProperty() 12 | : Super() 13 | { 14 | bIsEnabled = true; 15 | } 16 | 17 | bool UBUIEditorValidator_RequiredProperty::CanValidateAsset_Implementation(UObject* InAsset) const 18 | { 19 | UClass* AssetClass = nullptr; 20 | UBlueprint* Bp = Cast(InAsset); 21 | if (Bp && Bp->ParentClass) 22 | { 23 | AssetClass = Bp->ParentClass; 24 | } 25 | else 26 | { 27 | AssetClass = InAsset->GetClass(); 28 | } 29 | for (TFieldIterator PropertyIterator(AssetClass); PropertyIterator; ++PropertyIterator) 30 | { 31 | if (PropertyIterator->GetBoolMetaData(PropertyName)) 32 | { 33 | return true; 34 | } 35 | } 36 | return false; 37 | } 38 | 39 | EDataValidationResult UBUIEditorValidator_RequiredProperty::ValidateLoadedAsset_Implementation( 40 | UObject* InAsset, TArray& ValidationErrors) 41 | { 42 | bool bAnyFailed = false; 43 | bool bAnyChecked = false; 44 | 45 | UClass* AssetClass = nullptr; 46 | UBlueprint* Bp = Cast(InAsset); 47 | if (Bp && Bp->ParentClass) 48 | { 49 | AssetClass = Bp->GeneratedClass; 50 | } 51 | else 52 | { 53 | AssetClass = InAsset->GetClass(); 54 | } 55 | 56 | UObject* MyCDO = AssetClass->GetDefaultObject(true); 57 | for (TFieldIterator PropertyIterator(AssetClass); PropertyIterator; ++PropertyIterator) 58 | { 59 | if (PropertyIterator->GetBoolMetaData(PropertyName)) 60 | { 61 | bAnyChecked = true; 62 | FObjectPropertyBase* ObjProp = CastField(*PropertyIterator); 63 | if (ObjProp) 64 | { 65 | UObject* Something = ObjProp->GetObjectPropertyValue_InContainer(MyCDO); 66 | 67 | if (Something == nullptr) 68 | { 69 | bAnyFailed = true; 70 | AssetFails(InAsset, FText::FormatNamed( 71 | LOCTEXT("BUIValidatorError_NotSet", "Property '{PropertyName}' is not set. All variables marked with '{BUIMetaName}' must be set to non-null"), 72 | "PropertyName", ObjProp->GetDisplayNameText(), 73 | "BUIMetaName", FText::FromName(PropertyName)), 74 | ValidationErrors); 75 | } 76 | } 77 | } 78 | } 79 | 80 | if (!bAnyChecked) 81 | return EDataValidationResult::NotValidated; 82 | 83 | if (!bAnyFailed) 84 | { 85 | AssetPasses(InAsset); 86 | } 87 | return bAnyFailed ? EDataValidationResult::Invalid : EDataValidationResult::Valid; 88 | } 89 | 90 | #undef LOCTEXT_NAMESPACE 91 | -------------------------------------------------------------------------------- /Source/BUIValidator/Private/BUIEditorValidator_Textures.cpp: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #include "BUIEditorValidator_Textures.h" 4 | #include "Engine/Texture2D.h" 5 | #include "Editor/EditorPerProjectUserSettings.h" 6 | #include "EditorFramework/AssetImportData.h" 7 | #include "BUIValidatorSettings.h" 8 | 9 | #define LOCTEXT_NAMESPACE "BUIEditorValidator" 10 | 11 | UBUIEditorValidator_Textures::UBUIEditorValidator_Textures() 12 | : Super() 13 | { 14 | bIsEnabled = true; 15 | } 16 | 17 | bool UBUIEditorValidator_Textures::CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject* InObject, FDataValidationContext& InContext) const 18 | { 19 | const UBUIValidatorSettings& ValidatorSettings = *GetDefault(); 20 | return ValidatorSettings.ShouldValidateAsset(InObject); 21 | } 22 | 23 | bool GetIsPowerOfTwo(int32 Num) 24 | { 25 | return (Num & (Num - 1)) == 0; 26 | } 27 | 28 | EDataValidationResult UBUIEditorValidator_Textures::ValidateLoadedAsset_Implementation(const FAssetData& InAssetData, UObject* InAsset, FDataValidationContext& Context) 29 | { 30 | bool bAnyFailed = false; 31 | bool bAnyChecked = false; 32 | 33 | UTexture2D* Texture = Cast(InAsset); 34 | if (Texture) 35 | { 36 | const FString ImportAssetPath = Texture->AssetImportData->GetFirstFilename(); 37 | const UBUIValidatorSettings& ValidatorSettings = *GetDefault(); 38 | 39 | // First see if this matches 40 | for (const auto& Group : ValidatorSettings.ValidationGroups) 41 | { 42 | if (Group.ShouldGroupValidateAsset(InAsset)) 43 | { 44 | if (Group.ValidationRule.TextureGroups.Num()) 45 | { 46 | bAnyChecked = true; 47 | 48 | if (!Group.ValidationRule.TextureGroups.Contains(Texture->LODGroup)) 49 | { 50 | bAnyFailed = true; 51 | TArray TextureGroupNames; 52 | UEnum* TextureGroupEnum = StaticEnum(); 53 | for (const auto& TextureGroup : Group.ValidationRule.TextureGroups) 54 | { 55 | TextureGroupNames.Add(TextureGroupEnum->GetMetaData(TEXT("DisplayName"), TextureGroup)); 56 | } 57 | AssetFails(InAsset, FText::Format( 58 | LOCTEXT("BUIValidatorError_TextureGroup", "Texture asset texture group must be '{0}', but is '{1}'"), 59 | FText::FromString(FString::Join(TextureGroupNames, TEXT(", "))), 60 | FText::FromString(TextureGroupEnum->GetMetaData(TEXT("DisplayName"), Texture->LODGroup)))); 61 | } 62 | } 63 | 64 | if (Group.ValidationRule.PixelFormats.Num()) 65 | { 66 | bAnyChecked = true; 67 | if (!Group.ValidationRule.PixelFormats.Contains(Texture->GetPixelFormat())) 68 | { 69 | bAnyFailed = true; 70 | TArray PixelFormatNames; 71 | for (const auto& PixelFormat : Group.ValidationRule.PixelFormats) 72 | { 73 | PixelFormatNames.Add(UEnum::GetValueAsString(PixelFormat)); 74 | } 75 | AssetFails(InAsset, FText::Format( 76 | LOCTEXT("BUIValidatorError_PixelFormat","Texture asset pixel format must be '{0}', but is '{1}'"), 77 | FText::FromString(FString::Join(PixelFormatNames, TEXT(", "))), 78 | FText::FromString(UEnum::GetValueAsString(Texture->GetPixelFormat())))); 79 | } 80 | } 81 | 82 | if (Group.ValidationRule.CompressionSettings.Num()) 83 | { 84 | bAnyChecked = true; 85 | if (!Group.ValidationRule.CompressionSettings.Contains(Texture->CompressionSettings)) 86 | { 87 | bAnyFailed = true; 88 | TArray CompressionSettingNames; 89 | UEnum* CompressionSettingsEnum = StaticEnum(); 90 | for (const auto& CompressionSetting : Group.ValidationRule.CompressionSettings) 91 | { 92 | CompressionSettingNames.Add( 93 | CompressionSettingsEnum->GetMetaData(TEXT("DisplayName"), CompressionSetting)); 94 | } 95 | AssetFails(InAsset, FText::Format( 96 | LOCTEXT("BUIValidatorError_PixelFormat","Texture asset pixel format must be '{0}', but is '{1}'"), 97 | FText::FromString(FString::Join(CompressionSettingNames, TEXT(", "))), 98 | FText::FromString(CompressionSettingsEnum->GetMetaData(TEXT("DisplayName"), Texture->CompressionSettings)))); 99 | } 100 | } 101 | 102 | if (Group.ValidationRule.MipGenSettings.Num()) 103 | { 104 | bAnyChecked = true; 105 | if (!Group.ValidationRule.MipGenSettings.Contains(Texture->MipGenSettings)) 106 | { 107 | bAnyFailed = true; 108 | TArray MipGenSettingNames; 109 | UEnum* MipGenSettingsEnum = StaticEnum(); 110 | for (const auto& MipGenSetting : Group.ValidationRule.MipGenSettings) 111 | { 112 | MipGenSettingNames.Add(MipGenSettingsEnum->GetMetaData(TEXT("DisplayName"), MipGenSetting)); 113 | } 114 | AssetFails(InAsset, FText::Format( 115 | LOCTEXT("BUIValidatorError_PixelFormat", "Texture asset mip gen settings must be '{0}', but is '{1}'"), 116 | FText::FromString(FString::Join(MipGenSettingNames, TEXT(", "))), 117 | FText::FromString(MipGenSettingsEnum->GetMetaData(TEXT("DisplayName"), Texture->MipGenSettings)))); 118 | } 119 | } 120 | 121 | if (Group.ValidationRule.TextureSizeRequirements.Num() > 0) 122 | { 123 | bAnyChecked = true; 124 | if (Group.ValidationRule.TextureSizeRequirements.Contains(EBUITextureSizeRequirement::MultipleOfFour) 125 | && (Texture->GetSizeX() % 4 != 0 || Texture->GetSizeY() % 4 != 0)) 126 | { 127 | bAnyFailed = true; 128 | AssetFails(InAsset, FText::Format( 129 | LOCTEXT("BUIValidatorError_MultipleOfFour", 130 | "Texture asset size must be a multiple of 4, but is {0}x{1}"), 131 | FText::AsNumber(Texture->GetSizeX(), &FNumberFormattingOptions::DefaultNoGrouping()), 132 | FText::AsNumber(Texture->GetSizeY(), &FNumberFormattingOptions::DefaultNoGrouping()))); 133 | } 134 | else if (Group.ValidationRule.TextureSizeRequirements.Contains(EBUITextureSizeRequirement::PowerOfTwo) 135 | && (!GetIsPowerOfTwo(Texture->GetSizeX()) || !GetIsPowerOfTwo(Texture->GetSizeY()))) 136 | { 137 | bAnyFailed = true; 138 | AssetFails(InAsset, FText::Format( 139 | LOCTEXT("BUIValidatorError_PowerOfTwo", "Texture asset size must be a power of two, but is {0}x{1}"), 140 | FText::AsNumber(Texture->GetSizeX(), &FNumberFormattingOptions::DefaultNoGrouping()), 141 | FText::AsNumber(Texture->GetSizeY(), &FNumberFormattingOptions::DefaultNoGrouping()))); 142 | } 143 | } 144 | 145 | if (Group.ValidationRule.Prefixes.Num() > 0) 146 | { 147 | bAnyChecked = true; 148 | const FString Filename = FPaths::GetCleanFilename(ImportAssetPath); 149 | bool bAnyMatched = false; 150 | for (const auto& Prefix : Group.ValidationRule.Prefixes) 151 | { 152 | if (Filename.StartsWith(Prefix)) 153 | { 154 | bAnyMatched = true; 155 | } 156 | } 157 | if (!bAnyMatched) 158 | { 159 | bAnyFailed = true; 160 | AssetFails(InAsset, FText::Format( 161 | LOCTEXT("BUIValidatorError_Prefix", "Texture asset name must be prefixed with '{0}', but is '{1}'"), 162 | FText::FromString(FString::Join(Group.ValidationRule.Prefixes, TEXT(", "))), 163 | FText::FromString(Filename))); 164 | } 165 | } 166 | 167 | if (Group.ValidationRule.Suffixes.Num() > 0) 168 | { 169 | bAnyChecked = true; 170 | const FString Filename = FPaths::GetCleanFilename(ImportAssetPath); 171 | bool bAnyMatched = false; 172 | for (const auto& Suffix : Group.ValidationRule.Suffixes) 173 | { 174 | if (Filename.EndsWith(Suffix)) 175 | { 176 | bAnyMatched = true; 177 | } 178 | } 179 | if (!bAnyMatched) 180 | { 181 | bAnyFailed = true; 182 | AssetFails(InAsset, FText::Format( 183 | LOCTEXT("BUIValidatorError_Suffix", "Texture asset name must be suffixed with '{0}', but is '{1}'"), 184 | FText::FromString(FString::Join(Group.ValidationRule.Suffixes, TEXT(", "))), 185 | FText::FromString(Filename))); 186 | } 187 | } 188 | 189 | if (Group.ValidationRule.bRequireDataSourceFolder) 190 | { 191 | bAnyChecked = true; 192 | TArray AbsoluteFilenames = Texture->AssetImportData->ExtractFilenames(); 193 | 194 | const UEditorPerProjectUserSettings& EditorSettings = *GetDefault(); 195 | 196 | if (EditorSettings.DataSourceFolder.Path.IsEmpty()) 197 | { 198 | bAnyFailed = true; 199 | AssetFails(InAsset, LOCTEXT("BUIValidatorError_NoDataSourceFolder", "Data Source Folder must be set. Please set it in Editor Preferences")); 200 | } 201 | else if (!ImportAssetPath.StartsWith(EditorSettings.DataSourceFolder.Path)) 202 | { 203 | bAnyFailed = true; 204 | AssetFails(InAsset, FText::Format( 205 | LOCTEXT("BUIValidatorError_FileImportedOutsideDataSourceFolder", "Importing a file from '{0}', outside of Data Source Folder '{1}'"), 206 | FText::FromString(ImportAssetPath), 207 | FText::FromString(EditorSettings.DataSourceFolder.Path))); 208 | } 209 | } 210 | 211 | if (Group.ValidationRule.bSpecifySRGB) 212 | { 213 | bAnyChecked = true; 214 | 215 | if (Texture->SRGB != Group.ValidationRule.bSRGB) 216 | { 217 | bAnyFailed = true; 218 | AssetFails(InAsset, FText::Format( 219 | LOCTEXT("BUIValidatorError_SRGBMismatch", "Texture asset must have sRGB set to '{0}', was set to '{1}'"), 220 | Group.ValidationRule.bSRGB ? LOCTEXT("True", "True") : LOCTEXT("False", "False"), 221 | Texture->SRGB ? LOCTEXT("True", "True") : LOCTEXT("False", "False"))); 222 | } 223 | } 224 | } 225 | } 226 | } 227 | 228 | if (!bAnyChecked) 229 | return EDataValidationResult::NotValidated; 230 | 231 | if (!bAnyFailed) 232 | { 233 | AssetPasses(InAsset); 234 | } 235 | return bAnyFailed ? EDataValidationResult::Invalid : EDataValidationResult::Valid; 236 | } 237 | 238 | #undef LOCTEXT_NAMESPACE 239 | -------------------------------------------------------------------------------- /Source/BUIValidator/Private/BUIValidateAllCommandlet.cpp: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #include "BUIValidateAllCommandlet.h" 4 | 5 | DEFINE_LOG_CATEGORY(LogCommandletPlugin); 6 | 7 | UBUIValidateAllCommandlet::UBUIValidateAllCommandlet() 8 | { 9 | HelpDescription = "Runs existing validation rules on all assets"; 10 | HelpWebLink = "https://github.com/benui-dev/UE-BUIValidator"; 11 | 12 | IsClient = false; 13 | IsEditor = false; 14 | IsServer = false; 15 | LogToConsole = true; 16 | } 17 | 18 | int32 UBUIValidateAllCommandlet::Main(const FString& Params) 19 | { 20 | #if 0 21 | UE_LOG(LogCommandletPlugin, Display, TEXT("Hello world!")); 22 | 23 | FBUIValidatorModule& ValidatorModule = FModuleManager::LoadModuleChecked("BUIValidator"); 24 | //AssetRegistryModule.Get().SearchAllAssets(true); 25 | 26 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); 27 | AssetRegistryModule.Get().SearchAllAssets(true); 28 | 29 | // Load paths from settings 30 | const UBUIValidatorSettings& ValidatorSettings = *GetDefault(); 31 | for ( const auto& Group : ValidatorSettings.ValidationGroups ) 32 | { 33 | if ( Group.bRunInCommandlet 34 | && Group.ShouldGroupValidateAsset() 35 | { 36 | if ( Group.ValidationRule.TextureGroups.Num() > 0 ) 37 | { 38 | Texture->LODGroup = Group.ValidationRule.TextureGroups[ 0 ]; 39 | } 40 | 41 | if ( Group.ValidationRule.CompressionSettings.Num() > 0 ) 42 | { 43 | Texture->CompressionSettings = Group.ValidationRule.CompressionSettings[ 0 ]; 44 | } 45 | 46 | if ( Group.ValidationRule.MipGenSettings.Num() > 0 ) 47 | { 48 | Texture->MipGenSettings = Group.ValidationRule.MipGenSettings[ 0 ]; 49 | } 50 | } 51 | } 52 | 53 | 54 | FARFilter Filter; 55 | Filter.ClassNames.Add(UTexture::StaticClass()->GetFName()); 56 | 57 | AssetRegistryModule.Get().EnumerateAssets(Filter, [FoundAssets = &AssetData, InInterfaceClass](const FAssetData& InAssetData) 58 | { 59 | if (ImplementsInterface(InAssetData, InInterfaceClass)) 60 | { 61 | FoundAssets->Add(InAssetData); 62 | } 63 | 64 | return true; 65 | }); 66 | 67 | #endif 68 | return 0; 69 | } -------------------------------------------------------------------------------- /Source/BUIValidator/Private/BUIValidateAllCommandlet.h: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Commandlets/Commandlet.h" 7 | #include "BUIValidateAllCommandlet.generated.h" 8 | 9 | DECLARE_LOG_CATEGORY_EXTERN(LogCommandletPlugin, Log, All); 10 | 11 | UCLASS() 12 | class UBUIValidateAllCommandlet : public UCommandlet 13 | { 14 | GENERATED_BODY() 15 | public: 16 | UBUIValidateAllCommandlet(); 17 | 18 | // Begin UCommandlet interface 19 | virtual int32 Main(const FString& Params) override; 20 | // End UCommandlet interface 21 | }; 22 | -------------------------------------------------------------------------------- /Source/BUIValidator/Private/BUIValidatorModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #include "BUIValidatorModule.h" 4 | #include "BUIValidatorSettings.h" 5 | #include "ISettingsModule.h" 6 | #include "ISettingsSection.h" 7 | #include "ISettingsContainer.h" 8 | #include "Subsystems/ImportSubsystem.h" 9 | 10 | #define LOCTEXT_NAMESPACE "FBUIValidatorModule" 11 | 12 | void FBUIValidatorModule::StartupModule() 13 | { 14 | if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) 15 | { 16 | ISettingsContainerPtr SettingsContainer = SettingsModule->GetContainer("Project"); 17 | ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings( 18 | "Project", 19 | "Plugins", 20 | "BUI Validator", 21 | LOCTEXT("RuntimeGeneralSettingsName", "BUI Validator"), 22 | LOCTEXT("RuntimeGeneralSettingsDescription", "Configure UI data asset validation."), 23 | GetMutableDefault()); 24 | 25 | if (SettingsSection.IsValid()) 26 | { 27 | SettingsSection->OnModified().BindRaw(this, &FBUIValidatorModule::HandleSettingsSaved); 28 | } 29 | } 30 | 31 | FCoreDelegates::OnPostEngineInit.AddRaw(this, &FBUIValidatorModule::OnPostEngineInit); 32 | } 33 | 34 | void FBUIValidatorModule::ShutdownModule() 35 | { 36 | if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) 37 | { 38 | SettingsModule->UnregisterSettings("Project", "Project", "General"); 39 | } 40 | 41 | if (GIsEditor) 42 | { 43 | if (GEditor && GEditor->GetEditorSubsystem()) 44 | GEditor->GetEditorSubsystem()->OnAssetPostImport.RemoveAll(this); 45 | } 46 | } 47 | 48 | void FBUIValidatorModule::OnPostEngineInit() 49 | { 50 | if (GIsEditor) 51 | { 52 | GEditor->GetEditorSubsystem()->OnAssetPostImport.AddRaw( 53 | this, &FBUIValidatorModule::OnObjectReimported); 54 | } 55 | } 56 | 57 | void FBUIValidatorModule::OnObjectReimported(UFactory* ImportFactory, UObject* InObject) 58 | { 59 | UTexture2D* Texture = Cast(InObject); 60 | if (!Texture) 61 | return; 62 | 63 | // Only apply defaults to newly-imported assets 64 | const UBUIValidatorSettings& ValidatorSettings = *GetDefault(); 65 | for (const auto& Group : ValidatorSettings.ValidationGroups) 66 | { 67 | if (Group.bApplyOnImport 68 | && Group.ShouldGroupValidateAsset(InObject)) 69 | { 70 | if (Group.ValidationRule.TextureGroups.Num() > 0 && !Group.ValidationRule.TextureGroups.Contains( 71 | Texture->LODGroup)) 72 | { 73 | Texture->LODGroup = Group.ValidationRule.TextureGroups[0]; 74 | } 75 | 76 | if (Group.ValidationRule.CompressionSettings.Num() > 0 && !Group.ValidationRule.CompressionSettings. 77 | Contains(Texture->CompressionSettings)) 78 | { 79 | Texture->CompressionSettings = Group.ValidationRule.CompressionSettings[0]; 80 | } 81 | 82 | if (Group.ValidationRule.MipGenSettings.Num() > 0 && !Group.ValidationRule.MipGenSettings.Contains( 83 | Texture->MipGenSettings)) 84 | { 85 | Texture->MipGenSettings = Group.ValidationRule.MipGenSettings[0]; 86 | } 87 | } 88 | } 89 | } 90 | 91 | bool FBUIValidatorModule::HandleSettingsSaved() 92 | { 93 | UBUIValidatorSettings* Settings = GetMutableDefault(); 94 | bool ResaveSettings = false; 95 | 96 | // You can put any validation code in here and resave the settings in case an invalid 97 | // value has been entered 98 | 99 | if (ResaveSettings) 100 | { 101 | Settings->SaveConfig(); 102 | } 103 | 104 | return true; 105 | } 106 | 107 | #undef LOCTEXT_NAMESPACE 108 | 109 | IMPLEMENT_MODULE(FBUIValidatorModule, BUIValidator) 110 | -------------------------------------------------------------------------------- /Source/BUIValidator/Private/BUIValidatorSettings.cpp: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #include "BUIValidatorSettings.h" 4 | #include "Engine/Texture2D.h" 5 | 6 | #define LOCTEXT_NAMESPACE "BUIEditorValidator" 7 | 8 | bool UBUIValidatorSettings::ShouldValidateAsset(UObject* InAsset) const 9 | { 10 | UTexture2D* Texture = Cast(InAsset); 11 | if (Texture) 12 | { 13 | const UBUIValidatorSettings& ValidatorSettings = *GetDefault(); 14 | for (const auto& Group : ValidatorSettings.ValidationGroups) 15 | { 16 | if (Group.ShouldGroupValidateAsset(InAsset)) 17 | return true; 18 | } 19 | } 20 | 21 | return false; 22 | } 23 | 24 | bool FBUIValidatorGroup::ShouldGroupValidateAsset(UObject* InAsset) const 25 | { 26 | UTexture2D* Texture = Cast(InAsset); 27 | if (!Texture) 28 | return false; 29 | 30 | const FString AssetPathInUnreal = Texture->GetPathName(); 31 | 32 | bool bMatchAnyTextureGroup = MatchConditions.TextureGroups.Num() == 0 33 | || MatchConditions.TextureGroups.Contains(Texture->LODGroup); 34 | 35 | bool bMatchAnyPath = MatchConditions.Paths.Num() == 0; 36 | for (const auto& Path : MatchConditions.Paths) 37 | { 38 | if (AssetPathInUnreal.StartsWith(Path.Path)) 39 | { 40 | bMatchAnyPath = true; 41 | break; 42 | } 43 | } 44 | 45 | bool bMatchAnyPrefix = MatchConditions.Prefixes.Num() == 0; 46 | for (const auto& Prefix : MatchConditions.Prefixes) 47 | { 48 | if (FPaths::GetCleanFilename(AssetPathInUnreal).StartsWith(Prefix)) 49 | { 50 | bMatchAnyPrefix = true; 51 | break; 52 | } 53 | } 54 | 55 | bool bMatchAnySuffix = MatchConditions.Suffixes.Num() == 0; 56 | for (const auto& Suffix : MatchConditions.Suffixes) 57 | { 58 | if (FPaths::GetCleanFilename(AssetPathInUnreal).EndsWith(Suffix)) 59 | { 60 | bMatchAnySuffix = true; 61 | break; 62 | } 63 | } 64 | 65 | // Let's apply rules to this texture 66 | return bMatchAnyTextureGroup && bMatchAnyPath && bMatchAnyPrefix && bMatchAnySuffix; 67 | } 68 | 69 | #undef LOCTEXT_NAMESPACE 70 | -------------------------------------------------------------------------------- /Source/BUIValidator/Public/BUIEditorValidator_RequiredProperty.h: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "EditorValidatorBase.h" 6 | #include "BUIEditorValidator_RequiredProperty.generated.h" 7 | 8 | UCLASS(meta = ( DisplayName = "BUI Required Property Validator" )) 9 | class BUIVALIDATOR_API UBUIEditorValidator_RequiredProperty : public UEditorValidatorBase 10 | { 11 | GENERATED_BODY() 12 | 13 | public: 14 | UBUIEditorValidator_RequiredProperty(); 15 | 16 | protected: 17 | static const FName PropertyName; 18 | virtual bool CanValidateAsset_Implementation(UObject* InAsset) const override; 19 | virtual EDataValidationResult ValidateLoadedAsset_Implementation(UObject* InAsset, TArray& ValidationErrors) override; 20 | }; 21 | -------------------------------------------------------------------------------- /Source/BUIValidator/Public/BUIEditorValidator_Textures.h: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "EditorValidatorBase.h" 6 | #include "BUIEditorValidator_Textures.generated.h" 7 | 8 | struct FAssetData; 9 | 10 | UCLASS(meta = ( DisplayName = "BUI Texture Validator" )) 11 | class BUIVALIDATOR_API UBUIEditorValidator_Textures : public UEditorValidatorBase 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | UBUIEditorValidator_Textures(); 17 | 18 | protected: 19 | virtual bool CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject* InObject, FDataValidationContext& InContext) const override; 20 | virtual EDataValidationResult ValidateLoadedAsset_Implementation(const FAssetData& InAssetData, UObject* InAsset, FDataValidationContext& Context) override; 21 | }; 22 | -------------------------------------------------------------------------------- /Source/BUIValidator/Public/BUIValidatorModule.h: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | #include "Tickable.h" 8 | 9 | class FBUIValidatorModule : public IModuleInterface 10 | { 11 | public: 12 | // Begin IModuleInterface 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | 16 | virtual bool SupportsDynamicReloading() override { return true; } 17 | // End IModuleInterface 18 | 19 | protected: 20 | bool HandleSettingsSaved(); 21 | 22 | void OnPostEngineInit(); 23 | void OnObjectReimported( UFactory* ImportFactory, UObject* InObject ); 24 | }; 25 | -------------------------------------------------------------------------------- /Source/BUIValidator/Public/BUIValidatorSettings.h: -------------------------------------------------------------------------------- 1 | // Copyright ben ui. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "BUIValidatorSettings.generated.h" 6 | 7 | UENUM() 8 | enum class EBUITextureSizeRequirement 9 | { 10 | MultipleOfFour, 11 | PowerOfTwo, 12 | }; 13 | 14 | USTRUCT(meta = ( ToolTip = "All parts of a rule must pass in order for the rule to be applied" )) 15 | struct FBUIMatchConditions 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | // Match UTexture2D assets with any of these texture groups 21 | UPROPERTY(config, EditAnywhere, Category = Custom) 22 | TArray> TextureGroups = {TextureGroup::TEXTUREGROUP_UI}; 23 | 24 | // Match UTexture2D assets with any of these prefixes 25 | UPROPERTY(config, EditAnywhere, Category = Custom) 26 | TArray Prefixes = {"T_UI_"}; 27 | 28 | // Match UTexture2D assets with any of these suffixes 29 | UPROPERTY(config, EditAnywhere, Category = Custom) 30 | TArray Suffixes; 31 | 32 | // Match UTexture2D assets under any of these directories 33 | UPROPERTY(EditAnywhere, meta = ( ContentDir, TitleProperty = "Path" )) 34 | TArray Paths; 35 | }; 36 | 37 | USTRUCT() 38 | struct FBUIValidationRule 39 | { 40 | GENERATED_BODY() 41 | FBUIValidationRule() 42 | { 43 | // Defaults 44 | if (Paths.Num() == 0) 45 | { 46 | Paths.Add(FDirectoryPath()); 47 | Paths[0].Path = "/Game/UI"; 48 | } 49 | } 50 | 51 | public: 52 | // Textures must be in one of these Texture Groups 53 | UPROPERTY(config, EditAnywhere) 54 | TArray> TextureGroups = {TextureGroup::TEXTUREGROUP_UI}; 55 | 56 | // Textures must have one of these Compression Settings 57 | UPROPERTY(config, EditAnywhere) 58 | TArray> CompressionSettings = {TextureCompressionSettings::TC_Default}; 59 | 60 | // Textures must have one of these Pixel Formats 61 | UPROPERTY(config, EditAnywhere) 62 | TArray> PixelFormats = {EPixelFormat::PF_DXT5}; 63 | 64 | // Textures must have one of these mip gen settings 65 | UPROPERTY(config, EditAnywhere) 66 | TArray> MipGenSettings = {TextureMipGenSettings::TMGS_FromTextureGroup}; 67 | 68 | // Textures must have one of these prefixes. Is not applied on import 69 | UPROPERTY(config, EditAnywhere) 70 | TArray Prefixes = {"T_UI_"}; 71 | 72 | // Textures must have one of these suffixes. Is not applied on import 73 | UPROPERTY(config, EditAnywhere) 74 | TArray Suffixes; 75 | 76 | // Textures must pass these size requirements. Is not applied on import 77 | UPROPERTY(config, EditAnywhere) 78 | TSet TextureSizeRequirements = {EBUITextureSizeRequirement::MultipleOfFour}; 79 | 80 | // Textures must be within this path in Unreal. Is not applied on import 81 | UPROPERTY(EditAnywhere, meta = ( ContentDir )) 82 | TArray Paths; 83 | 84 | // Require that the Data Source Folder be set in Editor Preferences, and that assets are imported from there 85 | UPROPERTY(EditAnywhere) 86 | bool bRequireDataSourceFolder = true; 87 | 88 | // Require that the texture 89 | UPROPERTY(EditAnywhere) 90 | bool bSpecifySRGB = false; 91 | 92 | // Require that the texture 93 | UPROPERTY(EditAnywhere, meta=(EditCondition="bSpecifySRGB", EditConditionHides )) 94 | bool bSRGB = true; 95 | }; 96 | 97 | USTRUCT() 98 | struct FBUIValidatorGroup 99 | { 100 | GENERATED_BODY() 101 | 102 | public: 103 | UPROPERTY(config, EditAnywhere) 104 | FString GroupName; 105 | 106 | // Apply this group's rules to newly-imported assets 107 | UPROPERTY(config, EditAnywhere) 108 | bool bApplyOnImport = false; 109 | 110 | // Apply this group's rules when running BUIValidateAll from the commandlet 111 | UPROPERTY(config, EditAnywhere) 112 | bool bRunInCommandlet = true; 113 | 114 | UPROPERTY(config, EditAnywhere, Category = "Validation", meta = ( ShowOnlyInnerProperties )) 115 | FBUIMatchConditions MatchConditions; 116 | 117 | UPROPERTY(config, EditAnywhere, Category = "Validation", meta = ( ShowOnlyInnerProperties )) 118 | FBUIValidationRule ValidationRule; 119 | 120 | bool ShouldGroupValidateAsset(UObject* InAsset) const; 121 | }; 122 | 123 | UCLASS(config = Game, defaultconfig) //, AutoExpandCategories = "Validation" ) 124 | class UBUIValidatorSettings : public UObject 125 | { 126 | GENERATED_BODY() 127 | 128 | public: 129 | UPROPERTY(config, EditAnywhere, Category = "Validation", meta = ( TitleProperty = "GroupName", ShowOnlyInnerProperties )) 130 | TArray ValidationGroups; 131 | 132 | bool ShouldValidateAsset(UObject* InAsset) const; 133 | }; 134 | --------------------------------------------------------------------------------