前言
上周的时候,有网友问过我相关的导出场景模型的问题。 当时他给我发了一篇微信公众号的文章 链接 导出需要附带 矩阵信息 实在是太麻烦了,其实并不需要如此的复杂。 碍于时间实在是太仓促,没有时间详细分析我这边具体的操作流程。 遂抽空总结了一波,并且在总结的逛论坛的时候发现了更好导出的方案。
MergeStaticActor 方案
最开始分析这个问题,我首先想到的是对 Actor 右键能否直接导出模型。 然而却没有发现相关的方案,于是只能采用对 StaticMesh 等静态资源导出模型的方案了。 但是采用这个方案,通过遍历场景的模型可以找到关联的静态模型,导出却没有了 场景的 位置,只能按照上面链接的方式,将矩阵重新实现一遍吗? 经过我对 Actor 右键的研究,我发现可以通过 Merge Actors
合并生成一个带世界坐标位置的新 StaticMesh ,通过这个 Mesh 导出 FBX 就是带世界坐标位置的模型了。
配置窗口勾选 Pivot Point at Zero
点击下方的 Merge Actors
会生成一个带世界坐标的 StaticMesh 静态模型资产。 随后右键进行导出即可。
使用这个方案需要遍历场景中需要导出的资源,然后将临时的静态资源生成到临时的目录里,然后再逐个导出即可,最后再把临时目录删除干净。 Python 都提供了相应的 API 可以批量处理执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 level_lib = unreal.EditorLevelLibrary asset_lib = unreal.EditorAssetLibrary selected_static_actors = [ a for a in level_lib.get_selected_level_actors() if isinstance (a, unreal.StaticMeshActor) ] options = unreal.EditorScriptingMergeStaticMeshActorsOptions() options.set_editor_property("destroy_source_actors" , False ) options.set_editor_property("spawn_merged_actor" , False ) setting = unreal.MeshMergingSettings() setting.set_editor_property("pivot_point_at_zero" , True ) options.set_editor_property("mesh_merging_settings" , setting) fbx_exporter = unreal.StaticMeshExporterFBX() fbx_option = unreal.FbxExportOption() fbx_option.export_morph_targets = False fbx_option.export_preview_mesh = False fbx_option.level_of_detail = False fbx_option.collision = False fbx_option.export_local_time = False fbx_option.ascii = False fbx_option.vertex_color = True temp_directory = "/Game/Temp_FBX_Export" export_path = r"C:\FBX_EXPORT" for actor in selected_static_actors: actor_name = actor.get_name() asset_path = posixpath.join(temp_directory, actor_name) options.set_editor_property("base_package_name" , asset_path) level_lib.merge_static_mesh_actors([actor], options) fbx_path = os.path.join(export_path, "%s.fbx" % actor_name) mesh = unreal.load_asset(asset_path) task = unreal.AssetExportTask() task.set_editor_property("object" , mesh) task.set_editor_property("filename" , fbx_path) task.set_editor_property("exporter" , fbx_exporter) task.set_editor_property("automated" , True ) task.set_editor_property("prompt" , False ) task.set_editor_property("options" , fbx_option) unreal.Exporter.run_asset_export_task(task) asset_lib.delete_directory(temp_directory)
上面的代码就可以实现选中场景的静态模型导出 FBX 的效果,每个 Actor 都是单独的 Mesh 进行命名导出。
导出贴图资源
可以看到上面的方案导出如果是没有带上贴图的,如果需要贴图,需要从 Unreal 的材质里面查找。 这里我可以通过依赖关系的方式找到模型关联的材质。 然后也通过依赖的方式将关联的贴图全部导出来。 这样可以实现宁可误杀,绝不放过的将所有贴图导出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 mesh_lib = unreal.EditorStaticMeshLibrary def ls_dependencies (path ): data = asset_lib.find_asset_data(path) options = unreal.AssetRegistryDependencyOptions() dependencies = asset_registry.get_dependencies(data.package_name, options) return dependencies total = mesh_lib.get_number_materials(mesh) for i in range (total): material = mesh.get_material(i) textures = ls_dependencies(material.get_path_name()) for texture_path in textures: data = asset_lib.find_asset_data(texture_path) if not issubclass (getattr (unreal,str (data.asset_class)), unreal.Texture): continue texture_name = str (data.asset_name) tga_path = os.path.join(texture_folder, "%s.tga" % texture_name) tga_exporter = unreal.TextureExporterTGA() mesh = unreal.load_asset(asset_path) task = unreal.AssetExportTask() task.set_editor_property("object" , data.get_asset()) task.set_editor_property("filename" , tga_path) task.set_editor_property("exporter" , tga_exporter) task.set_editor_property("automated" , True ) task.set_editor_property("prompt" , False ) task.set_editor_property("options" , fbx_option) unreal.Exporter.run_asset_export_task(task)
通过 ls_dependencies 的方式将静态资产关联的所有资产全部罗列出来。 然后通过 AssetData 获取相关的数据。 最后可以获取到关联在材质上的贴图导出。 如果想要在 DCC 重建贴图需要将相关的 Diffuse Normal 贴图信息导出去,比如用 Json 进行存储,然后 DCC 端写一个读取配置工具。根据记录的贴图数据将关联的贴图贴到 DCC 材质的相关贴图通道上。
直接导出场景模型方案
上面的方案有个缺点,只能导出静态资产,如果是 SkeletalMesh 或者是 Foliage 相关的 Mesh 都无法导出。 因为 EditorScriptingMergeStaticMeshActorsOptions
这个类只对 静态模型 起作用。 然而 Unreal 的 Level 是可以直接导出场景所有的资产的。 那么有没有解决问题的办法呢? 非常凑巧,最近逛 Unreal 的论坛还真就找到了更加简单的方法。
https://forums.unrealengine.com/t/python-convert-actors-to-static-mesh/152073
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import unreallevel_lib = unreal.EditorLevelLibrary output_file = r'C:\FBX_EXPORT\ue4_output.fbx' selected_actors = level_lib.get_selected_level_actors() if len (selected_actors) == 0 : print ("No actor selected, nothing to export" ) quit() task = unreal.AssetExportTask() task.object = level_lib.get_editor_world() task.filename = output_file task.selected = True task.replace_identical = False task.prompt = False task.automated = True task.options = unreal.FbxExportOption() task.options.vertex_color = False task.options.collision = False task.options.level_of_detail = False unreal.Exporter.run_asset_export_task(task)
使用这个方案可以导出 Foliage landscape 等特殊的 Actor
只是使用这个方案查询材质需要在 关联的 component 下进行操作。
1 2 3 4 5 6 7 8 for actor in unreal.EditorLevelLibrary.get_selected_level_actors(): comp = actor.root_component for comp in comp.get_children_components(True ): if isinstance (comp,unreal.StaticMeshComponent): mesh = comp.static_mesh print (mesh)
通过上面的代码可以获取到关联继承的 StaticMeshComponent 即便 InstancedFoliageActor 没有材质模型贴图的属性显示。
还是可以通过获取 actor 的 component 找到 FoliageInstancedStaticMeshComponent ,从而找到关联的 static_mesh
总结
至于在 DCC 里面重建贴图的方式没有细说。 其实可以通过通配符匹配 _N _D 后缀的贴图,然后将这些通用的贴图在 DCC 里面自动连接上。 主要通过 json 的配置描述每个 fbx 关联的贴图即可。