Implementation scheme of resource self-correction in UE

UE中资源自修正的设计与实现方案

In a previous article, I introduced the implementation and efficiency optimization of multi-stage and automated resource checks using ResScannerUE in UE.

This already allows for very flexible configuration of resource checks and timely alerts, with very powerful rule expression capabilities. However, they are still limited to the stage of discovering problems for reminders; although there are constraints to prevent submission, manual processing is still required. Especially in cases with a large amount of existing resources, the manpower involved in manual processing is very time-consuming.

Based on this situation, I designed a mechanism for automating resource corrections that can automatically fix resources after rules are established, ensuring that all scanned resources are set to the correct state.

This completes the final piece of the puzzle in my resource check solution and can be applied throughout the scanning process as needed. This article will introduce its design concept and implementation mechanism, as well as some practical application scenarios in projects.

What Specifically Needs to Be Done?

I hope ResScanner can support the following requirements of mine:

  1. When resource rules are sufficiently defined, non-compliant resources can be automatically fixed to reduce human involvement.
  2. Make functionality as generic as possible, so that special logic does not need to be written for most resources.
  3. Simple configuration, no coding required.
  4. For complex resource processing needs, support custom extensions.
  5. Automated batch processing.
  6. Ability to correct in real time within the editor, establishing mandatory constraints.

Resource scanning itself can be used as a filter. The ability of ResScanner to express complex rules has been thoroughly discussed in previous articles, so when rules are established, it becomes clear whether a resource needs automatic processing.

Taking a rule that checks the sRGB property of a texture based on a naming convention as an example:

Rule description: If the naming of a texture is not *_DA or *_D and its sRGB property is True, it can be automatically modified to False.

The subsequent content of this article will use this rule as an example to introduce the design concept and implementation logic of resource corrections.

Why Not Use Property Matrix?

UE actually has a method for batch modifying resource attributes. It requires selecting multiple resources in the ContentBrowser, right-clicking to find Asset Actions-Bulk Edit via Property Matrix:

This allows for batch editing of the properties of selected resources:

However, the modifications to the properties are not very intuitive. For example, in the BoneCompression configuration for resources of type AnimSequence, it is displayed as a string in Bulk Edit via Property Matrix, rather than as a resource, which is not very intuitive.

Its design intent seems to facilitate batch modifications for resources in the current directory of the engine, without the ability to select resources across multiple directories. Moreover, it can only be manually selected in the ContentBrowser, without precise control or automation based on specific rules.

For example, the following rule: check whether textures ending with _WRD have their LOD Group set to World. If it’s not World, it should be automatically processed. This cannot be achieved through Property Matrix, which can only edit, not condition-edit.

Implementation Principle

The screening process and processing logic are as follows:

Resource Screening

First, filter out matching resources from the resource scanning checks and pass them to the post-processor.

Using the previous rule, it’s necessary to check whether the current resource is a Texture and whether its name meets the following logical expression:

1
bIsTexture2D && !(endwith(_DA) || endwith(_D))

Implementing this filtering logic using ResScanner:

Then, it can correctly check if the resource matches:

Resource Post-Processing

Once a matching resource is detected, it can proceed to the logic for automatic correction. In my implementation, the post-processing configuration for each rule is an array that can specify multiple Classes:

It needs to specify a subclass inheriting from UScannerPostProcesser, which has a Processer interface that can be used to get rule information and the triggering resource of the rule. It can access the resource for modification and even allows writing custom logic for handling complex needs.

1
bool Processor(FScannerPostProcesserContext& Context);

Using a simple method, a post-processing rule specific to Texture2D can be written to modify the texture’s sRGB property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool UPP_TextureSRGB::Processor_Implementation(FScannerPostProcesserContext& Context)
{
bool bRetStatus = true;
bool bIsTexture2D = Context.ScanResult.AssetData.GetClass()->IsChildOf(UTexture2D::StaticClass());
if(bIsTexture2D)
{
UPackage* Package = Context.ScanResult.AssetData.GetPackage();
UObject* Asset = Context.ScanResult.AssetData.GetAsset();
UTexture2D* Tex = Cast<UTexture2D>(Asset);
if(Tex)
{
Tex->SRGB = false;
Package->SetDirtyFlag(true);
bRetStatus = UEditorLoadingAndSavingUtils::SavePackages({Package}, true);
}
}
return bRetStatus;
}

Then, it can be configured to execute in the rule’s post-processing array:

Testing the execution effect:

It’s evident that the resource matched the rules during saving, automatically changing the SRGB property to False.

General Attribute Replacement Implementation

Based on the previous introduction, we can now filter out the resources that need processing. However, handling each type of resource individually is still necessary, and I despise any processes that require special handling.

So, can we implement a method that abstracts the requirement of “modifying certain attributes of a resource” into a general form?

Absolutely! Under normal circumstances (excluding certain extreme implementations like TypeCustomization), attributes that can be edited in the editor are all marked for reflection, and editable attributes belong to a subset of all reflected properties.

Therefore, by establishing a reflection attribute replacement mechanism based on general types, we can achieve this.

Getting Reflection Attributes of a Type

In the article Analysis of Reflection Implementation in UE: Basic Concepts, it was mentioned that TFieldIterator can be used to iterate through reflection properties in a UClass:

1
2
3
4
5
for(TFieldIterator<FProperty> PropertyIter(GetClass());PropertyIter;++PropertyIter)  
{
FProperty* PropertyIns = *PropertyIter;
UE_LOG(LogTemp,Log,TEXT("Property Name: %s"),*PropertyIns->GetName());
}

You only need the UClass of the target type. In our case, this is straightforward because we can specify the type of resources to check when configuring scanning rules, allowing direct access to the UClass.

Then, a TypeCustomization implementation can be written to automatically retrieve all reflection properties from the UClass and create a ComboBox control for property selection.

The design process is as follows:

For each property, the storage method is a Name-Value structure:

1
2
3
4
5
struct FSCNPostPropertyFixer  
{
FString Name;
FString Value;
};

This structure is used to store the name of the property in the current UClass, along with the value to set that property. Both the Name and Value are FStrings because it’s a universal way to describe property information, converting values to strings and then back to their actual values when necessary.

UE’s FProperty has a function ExportTextItem that can export the values of reflection properties to string form:

1
Property->ExportTextItem(Result,Property->ContainerPtrToValuePtr<void*>(Obj),TEXT(""),NULL,0);

For example:

1
2
3
4
(Name="SRGB",Value="True")
(Name="LODGroup",Value="TEXTUREGROUP_World")
(Name="PowerOfTwoMode",Value="PadToPowerOfTwo")
(Name="CompressionSettings",Value="TC_Default")

This allows us to address the issue of universal type value storage.

Additionally, based on TypeCustomization, we can create actual type control for editing within the editor, allowing values to be imported from strings.
CompressionSettings control is of enumeration type
SRGB control is of boolean type
Bone compression configuration is a resource type

Details on its implementation are thoroughly discussed in the article Customization of Property Panels in Unreal Engine, so I won’t elaborate further in this article.

Importing and Overriding Attributes

Based on the prior content, we can specify attributes and their values for a certain UClass type.

So how do we replace a property described in Name-Value format (e.g., Texture2D‘s (Name="SRGB",Value="True")) in the actual resources?

Similarly, UE’s FProperty also has a function to import values from strings, corresponding to ExportTextItem:

1
Property->ImportText(*Text,Property->ContainerPtrToValuePtr<uint8>(Object),0,Object);

When copying and pasting properties in the editor’s Details panel, it actually exports the property value as a string and processes it using this function to import the property value during pasting.

You only need to obtain the address of the property value that needs to be modified in order to complete the value modification.

Final Implementation

The implementation principles have been written about extensively, so let’s look at the actual effect.

For rules that require post-processing of resource checks, enable Resource Scan Post-Processing. If properties of resources need to be modified, configure a PP_GeneralPropertyFixer in the post-processing array:

Then, create rule parameters, which will have a Properties element. By adding another Properties element, the properties specified by the current rule type will be listed automatically:

Select the corresponding attribute, and the appropriate value control for that attribute will be automatically created below, allowing for easy modification:

It feels as if you are truly editing that property.

Similarly, taking the previous rule as an example, modifying the SRGB value of the texture. The configuration looks like this:

Screening condition
Replace property value

The execution effect is the same as when I wrote it in C++ code, but this is a generic method.

Moreover, the trigger notification will also show the values before and after the modification, reminding the modifier of the changes made to the property:

Thus, for any type of resource property check and automatic correction, it can be achieved purely through configuration, without writing any code.

Case Studies

Constraining AnimSeq Compression Configuration

Taking the compression configuration of animation sequences as an example, usually, projects will integrate libraries like ACL for compressing Bones or Curves, which can improve compression rates and reduce package size.

Although the engine has the functionality to modify the default animation compression settings, you can add the following configuration in DefaultEngine.ini and modify it to use the resource path you want, such as changing it to ACL:

1
2
3
[Animation.DefaultObjectSettings]
BoneCompressionSettings="/ACLPlugin/ACLAnimBoneCompressionSettings"
CurveCompressionSettings="/ACLPlugin/ACLAnimCurveCompressionSettings"

However, this configuration will not make the default animation compression settings effective for resources that already exist and have been modified, as shown below:

Therefore, it is also necessary to correct existing resources and establish strong constraints during editing.

Naming Constraint on sRGB Values

This is also the rule that runs throughout this article, constraining the sRGB values of textures based on naming conventions, summarized as follows:

Naming Constraint on TextureGroup Types

Constraining the TextureGroup types based on the names of texture resources:

Execution effect:

Automation

Since the entire resource correction plan is based on the resource scanning capabilities of ResScanner, it can directly reuse the support of ResScanner in Cmdlet.
This point is consistent with the description in Multistage Automated Resource Check Solutions in UE, with the difference being that you need to enable Allow Post-Processing when executing the scan configuration.

Conclusion

This article introduces the design concept and implementation plan for automatic resource corrections based on ResScanner check rules and presents a generalized approach to configure and modify resource attributes.

In most cases, automated modifications to resource attributes can address many needs, while complex resource processing can have custom logic written as extensions. This can maximally reduce the time of human involvement, though it cannot entirely avoid it. Certain issues like abnormal reference relationships still require human judgment for proper handling, as the rules may not detail things to that extent. However, from the implementation architecture of tools, it can be achieved.

As long as the rules are sufficiently clear, automatic repairs can be implemented for any resource based on this plan!

The article is finished. If you have any questions, please comment and communicate.

Scan the QR code on WeChat and follow me.

Title:Implementation scheme of resource self-correction in UE
Author:LIPENGZHA
Publish Date:2023/10/18 14:45
Word Count:9.8k Words
Link:https://en.imzlp.com/posts/19901/
License: CC BY-NC-SA 4.0
Reprinting of the full article is prohibited.
Your donation will encourage me to keep creating!