Bootstrap Chameleon Logo

Extend an Assets Editor

Add Unique Features to a Specific Resource Editor

Editor Extensions

In addition to adding menu items to the main menu and ContentBrowser, you can also add menu items to specific resource editors, such as the Material Editor, Physics Asset Editor, and Control Rig Editor, to extend the functionality of the resources being edited.

The menu entry and menu items are configured in the MenuConfig.json file.

Currently supported resource editors include:

  • Material Editor
  • Physics Asset Editor
  • Control Rig Editor

Material Editor

Menu Extension in Unreal Engine Material Editor

Taking the Material Editor as an example, after adding the following configuration to the MenuConfig.json file, you can see the menu items in the Material Editor as shown in the image above.

MenuConfig.json

{: .my-border }

```JSON
"OnMaterialEditorMenu": {
    "name": "Python Menu On Material Editor",
    "items":
    [
        {
            "name": "TA Python Material Example",
            "items": [
                {
                    "name": "Print Editing Material / MF",
                    "command": "print(%asset_paths)"
                },
                {
                    "name": "Log Editing Nodes",
                    "command": "editing_asset = unreal.load_asset(%asset_paths[0]); unreal.PythonMaterialLib.log_editing_nodes(editing_asset)"
                },
                {
                    "name": "Selected Nodes --> global variable _r",
                    "command": "_r = unreal.PythonMaterialLib.get_selected_nodes_in_material_editor(unreal.load_asset(%asset_paths[0]))"
                },
                {
                    "name": "Selected Node --> _r",
                    "command": "_r = unreal.PythonMaterialLib.get_selected_nodes_in_material_editor(unreal.load_asset(%asset_paths[0]))[0]"
                }
            ]
        }
    ]
}

Unlike adding menu items to the main menu, there may be multiple Material Editor windows open in the Unreal Engine editor. Therefore, the Python code executed in the menu items needs to clearly distinguish the material resources currently being edited. This is done using the %asset_paths variable placeholder. Other variable placeholders in TAPython can be found here: Variable Placeholder

Looking at the specific configuration items, such as the first menu item "Print Editing Material / MF", the actual code it executes is print(%asset_paths), where %asset_paths is a variable placeholder that will be replaced by the actual value at runtime. The actual value here is an array of paths of the material resources currently being edited, such as ['/Game/LevelPrototyping/Materials/M_PrototypeGrid'].

NOTE
Usually, %asset_paths is an array of resource paths with a length of 1.

The second menu item "Log Editing Nodes":

{
    "name": "Log Editing Nodes",
    "command": "editing_asset = unreal.load_asset(%asset_paths[0]); unreal.PythonMaterialLib.log_editing_nodes(editing_asset)"
},

In the command here, the material resource being edited is obtained through unreal.load_asset, and then unreal.PythonMaterialLib.log_editing_nodes is called to print the node information of the material resource.

Other editing and modification operations related to material nodes can be found here: Manipulate Material Expression Nodes of Material with Python in Unreal Engine

Physics Asset Editor

Similar to the Material Editor, after adding the following configuration to the OnPhysicsAssetEditorMenu in MenuConfig.json, you can see the menu items in the Physics Asset Editor as shown in the image below.

Custom menu in Unreal Engine Physics Asset Editor

MenuConfig.json

"OnPhysicsAssetEditorMenu": {
    "name": "Python Menu On Physics Asset Editor",
    "items":
    [
        {
            "name": "TA Python Physics Asset Example",
            "items": [
                {
                    "name": "Print Physics Asset",
                    "command": "print(%asset_paths)"
                },
                {
                    "name": "Physics Tool",
                    "ChameleonTools": "../Python/ControlRiggingTools/physics_tools.json"
                }
            ]
        }
    ]
},

Note that not only Python code is executed through command, but also Chameleon tools are loaded through ChameleonTools. For example, the following is a simple tool for editing Bodies in a physics resource.

G28_physics_asset_tools_preview
{
    "name": "Physics Tool",
    "ChameleonTools": "../Python/ControlRiggingTools/physics_tools.json"
}

Careful users may have noticed that when configuring ChameleonTools: "Physics Tool", we did not specify a variable placeholder like %asset_paths, but the physics_tools tool needs to know the physics asset being edited. Let's look at how physics_tools.py handles this:

  • In the __init__ function of the tool, it actively calls self.update_physics_asset()
  • In the execute function of the tool, self.data.get_context_strings() gets the path of the physics asset being edited, and then loads the physics asset using unreal.load_asset.

The return value of self.data.get_context_strings() is a string, which we convert to a string array using eval. This string array is equivalent to the content of %asset_paths.

WARNING
This function returns a serialized format of a string array. This design is intended to accommodate future possibilities where other types of data may need to be passed to Python. This design can more flexibly handle different types of data.

class PhysicsTools(metaclass=Singleton):
    def __init__(self, jsonPath: str):
        self.jsonPath = jsonPath
        self.data = unreal.PythonBPLib.get_chameleon_data(self.jsonPath)
        self._physics_asset = None
        self.update_physics_asset()

    def update_physics_asset(self):
        context_strs = self.data.get_context_strings()
        if context_strs:
            context = eval(self.data.get_context_strings())
            if len(context ) == 1:
                asset = unreal.load_asset(context[0])
                self.physics_asset = asset
                print(f"self.physics_asset: {self.physics_asset}")
            else:
                unreal.log_warning("Physics context len != 1")
        else:
            assert False, "Can't get context"

The APIs for modifying Bodies in physics assets can be found in PythonPhysicsAssetLib

ControlRig Editor

Similarly, the OnControlRigEditorMenu field can be used to add menu items to the Control Rig editor. Here is a simple example

Menus created by TAPython in Unreal Engine Control Rig Editor

MenuConfig.json

    "OnControlRigEditorMenu": {
        "name": "Python Menu On Control Rig Editor",
        "items":
        [
            {
                "name": "TA Python ControlRig Example",
                "items": [
                    {
                        "name": "Print ControlRig",
                        "command": "print(%asset_paths)"
                    },
                    {
                        "name": "Rigging Tools",
                        "command": "unreal.ChameleonData.launch_chameleon_tool('../Python/ControlRiggingTools/control_rigging_tools.json'); chameleon_control_rig.set_control_rig(%asset_paths)",
                        "icon": {
                            "style": "ChameleonStyle",
                            "name": "Resources.Chameleon_32x_png"
                        }
                    }
                ]
            }
        ]
    },

As with the example in the Material Editor, %asset_paths is a variable placeholder for the path array of the Control Rig resources actually being edited.

In the Rigging Tools menu item, not only can we use the example from Physics Tool to get the path of the Control Rig resource being edited through self.data.get_context_strings(), but we can also use the %asset_paths variable placeholder to directly pass the path of the Control Rig resource being edited to the chameleon_control_rig Python tool instance in the command. The two methods are different, but the effect is the same.

Summary

  • By configuring OnMaterialEditorMenu, OnPhysicsAssetEditorMenu, and OnControlRigEditorMenu in MenuConfig.ini, we can add menu items to the Material Editor, Physics Asset Editor, and Control Rig Editor.
  • The %asset_paths variable placeholder allows us to get the actual resource path being edited in the command of the menu item.
  • The self.data.get_context_strings() in Chameleon tools returns a serialized string of the resource path array being edited.
  • PythonPhysicsAssetLib and PythonMaterialLib have APIs for physics assets and materials

Reference

Manipulate Material Expression Nodes of Material with Python in Unreal Engine