Unreal材质替换插件
在学习了 UnrealPython 基础教程后,尝试开发了一下小功能:把 A 物体的材质一一对应赋给 B 物体。
UE4 里面不能对物体选面上材质,一些对物体的操作只能从其他三维软件中进行。在 UE4 中,会把物体的材质信息记录在一个属性当中,对于两个材质分组一致的物体,可以通过替换材质球达到显示一致的效果。
同时,如果两个物体的材质球数量不一样的话,会对多出来的进行忽略,序号一致的进行一一对应。
功能实现
material_tool.py
# coding: utf-8
import unreal
def get_component(actor):
if actor.static_class() == unreal.StaticMeshActor.static_class():
return actor.static_mesh_component
elif actor.static_class() == unreal.SkeletalMeshActor.static_class():
return actor.skeletal_mesh_component
else:
print ("Error: unacceptable type: {}, {}".format(actor.name(), actor.static_class()))
return None
def world_actor_function():
selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors()
if len(selected_actors) < 2:
print ("please select more than two objects")
return
ori_actor = selected_actors[0]
des_actors = selected_actors[1:]
ori_component = get_component(ori_actor)
if not ori_component:
return
ori_materials_num = len(ori_component.get_materials())
for des_actor in des_actors:
des_component = get_component(des_actor)
if not des_component:
continue
for index in range(ori_materials_num):
index_material = ori_component.get_material(index)
des_component.set_material(index, index_material)
def asset_function():
selected_assets = unreal.EditorUtilityLibrary().get_selected_assets()
if len(selected_assets) < 2:
print( "please select more than two objects")
return
ori_asset = selected_assets[0]
des_assets = selected_assets[1:]
if ori_asset.static_class() == unreal.SkeletalMesh.static_class():
ori_mat = ori_asset.get_editor_property('materials')
for des_asset in des_assets:
des_mat = des_asset.get_editor_property('materials')
if len(ori_mat) > len(des_mat):
mat = ori_mat[:len(des_mat)]
elif len(ori_mat) < len(des_mat):
mat = des_mat
for i in range(len(ori_mat)):
mat[i] = ori_mat[i]
else:
mat = ori_mat
des_asset.set_editor_property('materials', mat)
elif ori_asset.static_class() == unreal.StaticMesh.static_class():
ori_materials_num = 0
loop = True
interface_list = []
while loop:
material = ori_asset.get_material(ori_materials_num)
if material:
interface_list.append(material)
ori_materials_num = ori_materials_num + 1
else:
loop = False
for des_asset in des_assets:
for index in range(ori_materials_num):
index_material = ori_asset.get_material(index)
des_asset.set_material(index, index_material)
else:
print ("Error: unacceptable type: {}, {}".format(ori_asset, ori_asset.static_class()))
def main(asset=True):
if asset:
# edit asset
asset_function()
else:
# edit world actor
world_actor_function()
注:
- 可以转化资产材质,也可以转化世界中物体的材质。
- 当选择物体类型不一致(StaticMesh / SkeletonMesh)时,在转换资产材质时会出错。
- 可以实现一赋多,一套材质赋给多个物体。
创建快捷菜单
create_mt_tool_menu.py
# coding: utf-8
import unreal
menus = unreal.ToolMenus.get()
# add contextbrowser menu
menu_name = "ContentBrowser.AssetContextMenu"
menu = menus.find_menu(menu_name)
entry = unreal.ToolMenuEntry(type=unreal.MultiBlockType.MENU_ENTRY)
entry.set_label("Convert Materials")
# NOTE 注册执行的命令
typ = unreal.ToolMenuStringCommandType.PYTHON
run_str = "import material_tool; material_tool.main()"
entry.set_string_command(typ, "", run_str)
menu.add_section('CustomControl', label='CustomMenu')
menu.add_menu_entry('CustomControl',entry)
# add viewport right clicked menu
viewport_menu_name = "LevelEditor.ActorContextMenu"
menu = menus.find_menu(viewport_menu_name)
entry = unreal.ToolMenuEntry(type=unreal.MultiBlockType.MENU_ENTRY)
entry.set_label("Convert Materials")
typ = unreal.ToolMenuStringCommandType.PYTHON
run_str = "import material_tool; material_tool.main(False)"
entry.set_string_command(typ, "", run_str)
menu.add_section('CustomControl', label='ComstomMenu')
menu.add_menu_entry('CustomControl',entry)
# add toolbar menu
toolbar_menu_name = "LevelEditor.LevelEditorToolBar"
menu = menus.find_menu(toolbar_menu_name)
menu.add_section('CustomControl', label='Convert Materials')
typ = unreal.ToolMenuStringCommandType.PYTHON
entry = unreal.ToolMenuEntry(type=unreal.MultiBlockType.TOOL_BAR_BUTTON)
entry.set_label("By Asset")
run_str = "import material_tool; material_tool.main()"
entry.set_string_command(typ, "", run_str)
menu.add_menu_entry('CustomControl',entry)
entry = unreal.ToolMenuEntry(type=unreal.MultiBlockType.TOOL_BAR_BUTTON)
entry.set_label("By World")
run_str = "import material_tool; material_tool.main(False)"
entry.set_string_command(typ, "", run_str)
menu.add_menu_entry('CustomControl',entry)
# add it to show the button
menus.refresh_all_widgets()
可以选择资产右键、或选择世界中物体右键、或在工具架上点击使用。
配置插件
一开始我是用 Startup Scripts 进行配置的,但是这种方式配置假如新建了项目,每次都要重新指定脚本路径,非常麻烦。
后来跟智伤帝探讨后发现可以像 Maya UserSetup 一样配置一个启动时自动运行的文件。创建一个名为 “init_unreal.py” 的文件,放置于我的文档 “Documents\UnrealEngine\Python” 文件夹中,Unreal 启动时就会自动读取该文件。
init_unreal.py
import create_mt_tool_menu
为了方便,我直接把这几个文件也放进“Documents\UnrealEngine\Python” 文件夹。
这样,只需要在创建新项目时加载 Python 插件,重启引擎后就可以在菜单上看到配置好的工具了。
能不能让引擎在创建新项目的时候自动加载 Python 插件呢(以尽量减少操作为目的)?答案是可以的,智伤帝给出了修改启动 bat 的方式,这种方式以后再研究使用吧,暂时是用不上了。
至于加载 Python,项目只要加载过一次,下次就会自动加载了,所以动动手指也没有特别麻烦啦~
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!