[UE4] Start to make own tools : find a way to move materials & textures of a foder in separate folders

Hi guys,

I would like to start to create my own tools in Unreal ( 4.27.2 for the moment )

And I would like to start with something simple :

Make a right click on a folder in Unreal and choose my function : “set materials and textures folders”
The function needs to :

  • move all materials in folders and subfolders of the selected folder in Selected-Folder/Materials
  • move all textures in folders and subfolders of the selected folder in Selected-Folder/Textures
  • make a “Fixup Redirectors” on the Selected-Folder and subfolders to avoid issues.
  • delete all folders become empty

How can I start ? What is the best way to do that ? ( Blueprint Utility ? Python ? both ? )
Thanks for your help

For assets you could go for AssetActionUtility, an editor utility that exposes it self in the context menu when right clicking assets. You could opt for either Python or Blueprint, they should be able to do the same.

However it seem Unreal don’t see Folders as Assets so afraid this is a C++ thing.

I think a bunch of that could be done in python or a mix of blutility + python scripts (depending on how you want to do things) - If you want to do right click things, you’ll have to delve into menu making which is/was horrendous in UE4 last time I did it (which was a year or so ago)

Luckily though, @robberyman and I went through that a while ago and there’s some code around

Yeah, seem you can actually add menu items to folders also. Debugger told me there was no name or menu existing until the context menu was visible so gave up previous attempt.

import unreal


@unreal.uclass()
class MyEntry(unreal.ToolMenuEntryScript):
    @unreal.ufunction(override=True)
    def execute(self, context):
        print("Hello!")


def main():
    menus = unreal.ToolMenus.get()

    menu_name = "ContentBrowser.FolderContextMenu"
    section_name = "PathViewFolderOptions"

    # create the tool menu entry script object,
    script_object = MyEntry()
    script_object.init_entry(
        owner_name = menu_name,
        menu = menu_name,
        section = section_name,
        name = "MyEntry",
        label = "My Entry",
        tool_tip = "This is a tool tip for My Entry"
    )
    script_object.register_menu_entry()  # not sure if needed to register,


if __name__ == "__main__":
    main()

This will create a menu entry in the folder context menu, then you can use EditorAssetUtility to do most of what you want.

Think the redirector part is something you will have to sort yourself however. Redirectors | Unreal Engine 4.27 Documentation

Yes! the call of MyEntry works like a charm @robberyman !
Thanks

Now, how can i retrieve the information for my script?

  • folders
  • subfolders
  • type of .asset if it is a material
  • type of .asset if it is a texture
  • move them to the appropriate directory
    ( folder where I clicked is the / of subfolders : Materials & Textures )
  • do the “Fixup Redirectors”

A bump guys @robberyman @Tool

I don’t understand how to link my working python call to a EditorAssetUtility ? :frowning:

First of all, what class I need to create like Editor Utilities Blueprint ?

Heeelp

Just make your working python call inside the execute method of the code above, it is what executes when you push the button.

@robberyman

Ok but how to do the call to a Blueprint Utility? with load_blueprint_class ?
And what I need to do in the blueprint to receive the path of the folder for example ?

Sorry but it’s very difficult to find any documentation about that…

Think you will have to give more context to what you’re attempting.

Do I understand you if you know have a menu, which you created from Python. And an Editor Utility Blueprint with a run method you want to call?

As for getting the Selected Folder Paths, Editor Utility Library has a method for this called just that. But why call this in a Blueprint when you also have it available in Python and can just call it in the menu you then have?

https://docs.unrealengine.com/5.1/en-US/PythonAPI/class/EditorUtilityLibrary.html#unreal.EditorUtilityLibrary.get_selected_folder_paths

Guess this is maybe not available to 4.27

TArray<FString> UEditorUtilityLibrary::GetSelectedFolderPaths()
{
	IContentBrowserSingleton& ContentBrowser = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser").Get();
	TArray<FString> Paths;
	ContentBrowser.GetSelectedFolders(Paths);
	return Paths;
}

Is the code if you want to expose a similar function in C++ yourself

@robberyman

Yes I work on 4.27.2 not 5.x

I forgot the menu for a time, and start with a simple script.
I succeed to do something in full Python, it’s not perfect but do the job

BUUUUT, If I closed the UE4, and restart it, materials lose the link to textures …
I don’t know how I can fix that, or even if it’s possible …

import unreal

prefixTexture2D             = "T_"
prefixMaterialInstance      = "MIC_"
workingPath = "/Game/TEST_IMPORT/BKP"

@unreal.uclass()
class MyEditorAssetLIbrary(unreal.EditorAssetLibrary):
    pass

def GetProperPrefix(className):
    _prefix = ""
    if className == "Texture2D":
        _prefix = prefixTexture2D
    elif className == "MaterialInstanceConstant":
        _prefix = prefixMaterialInstance
    else:
        _prefix = ""
    return  _prefix

editorAssetLib = MyEditorAssetLIbrary()

allAssets = editorAssetLib.list_assets(workingPath, True, False)
allAssetsCount = len(allAssets)

selectedAssetPath = workingPath

editorAssetLib.make_directory(workingPath+"/Textures_")
editorAssetLib.make_directory(workingPath+"/Materials_")

with unreal.ScopedSlowTask(allAssetsCount, selectedAssetPath) as ST:
    ST.make_dialog(True)

    for asset in allAssets:
        _assetData = editorAssetLib.find_asset_data(asset)
        _assetName = _assetData.get_asset().get_name()
        _assetPathName = _assetData.get_asset().get_path_name()

        _assetClassName = _assetData.get_asset().get_class().get_name()
        _assetPrefix = GetProperPrefix(_assetClassName)

        print("_assetClassName : "+_assetClassName)

        if _assetClassName == "MaterialInstanceConstant": _assetPathOnly = workingPath+"/Materials/"
        if _assetClassName == "Texture2D": _assetPathOnly = workingPath+"/textures/" 


        if _assetPrefix in _assetName:
            continue
        elif _assetPrefix == "":
            continue
        else:

            _targetPathName = _assetPathOnly + ("%s%s%s" % (_assetName, ".", _assetName))


            editorAssetLib.rename_asset(_assetPathName, _targetPathName)
            unreal.log(">>>>>> moving [%s] to [%s]" %(_assetPathName, _targetPathName))

        if ST.should_cancel():
            break
        ST.enter_progress_frame(1, asset)

Edit: interesting, if I open a material, save it again, and close it before restart UE4, I’ve not problem of “white material” anymore. Maybe if save asset after move them ( specially materials ) : it could be works. :woozy_face:

Edit2: YES! It was that, it works ! I added :

# force save asset to avoid f***g white materials !!
editorAssetLib.save_asset(_targetPathName)


Now it could be cool to add all of that to your menu @robberyman to get my “workingPath” on the fly, but I don’t know how to get it