import bpy import os from bpy.props import (StringProperty, PointerProperty, EnumProperty, BoolProperty) from bpy.types import (Panel, Operator, AddonPreferences, PropertyGroup) bl_info = \ { "name": "Unreal Exporter", "author": "Bozarre", "version": (1, 0, 0), "blender": (4, 2, 0), "location": "View 3D > Object Mode > Unreal Exporter", "description": "Batch exporter for Unreal Engine", "warning": "", "wiki_url": "", "tracker_url": "", "category": "Exporter", } class UEExporterSettings(PropertyGroup): path : StringProperty( #name="", description="Path to Directory (empty means same as curren file)", default="//", maxlen=1024, subtype='FILE_PATH') source_collection : StringProperty( description="Collection name (collisions don't have to be in the same collection", default="Export", maxlen=1024) collisions: BoolProperty( name="Export Collisions", description="Whether to export the collision shapes", default = True ) local_origin: BoolProperty( name="Export Origin", description="Whether to use the objects origin as the export origin", default = False ) class VIEW3D_PT_PanelExportAll(bpy.types.Panel): bl_label = "Export Selected" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "UE Exporter" bl_context = "objectmode" def draw(self, context): layout = self.layout scn = context.scene layout.label(text="Export Path Location:") layout.prop(scn.ue_exporter, "path", text="") layout.label(text="Collection to export") layout.prop(scn.ue_exporter, "source_collection", text="") layout.prop(scn.ue_exporter, "local_origin", text=" Export Origin") layout.prop(scn.ue_exporter, "collisions", text=" Export Collisions") layout.operator("ue_exporter_obs.separate_export", text='Export Seperate', icon='TRIA_RIGHT') layout.operator("ue_exporter_obs.combine_export", text='Export Combined', icon='TRIA_RIGHT') class OBJECT_OT_SeparateExport(bpy.types.Operator): bl_idname = "ue_exporter_obs.separate_export" bl_label = "Export Selected" bl_options = {"UNDO"} def execute(self, context): return {'FINISHED'} def invoke(self, context, event): SeparateExport(context.scene.ue_exporter.path) self.report({'INFO'}, 'Exported') return {'FINISHED'} class OBJECT_OT_CombineExport(bpy.types.Operator): bl_idname = "ue_exporter_obs.combine_export" bl_label = "Export Selected" bl_options = {"UNDO"} def execute(self, context): return {'FINISHED'} def invoke(self, context, event): CombineExport(context.scene.ue_exporter.path) self.report({'INFO'}, 'Did nothing (yet)') return {'FINISHED'} def SeparateExport(exportFolder): collection_collision_export = bpy.data.collections.new("Collision.Export") bpy.context.scene.collection.children.link(collection_collision_export) objects = bpy.data.collections[bpy.context.scene.ue_exporter.source_collection].all_objects bpy.ops.object.select_all(action='DESELECT') for object in list(objects): if object.type not in ['MESH']: continue if (('UCX_' + object.name) in bpy.data.objects): collision = bpy.data.objects['UCX_' + object.name] export_collision = collision.copy() export_collision.data = collision.data.copy() export_collision.animation_data_clear() bpy.context.collection.objects.link(export_collision) collection_collision_export.objects.link(export_collision) export_collision.select_set(True) bpy.context.view_layer.objects.active = export_collision bpy.ops.object.modifier_apply(modifier='GeometryNodes') bpy.ops.mesh.separate(type='LOOSE') object.select_set(True) exportName = bpy.path.abspath(exportFolder) + object.name + '.fbx' bpy.ops.export_scene.fbx(filepath=exportName, use_selection=True, mesh_smooth_type='FACE') bpy.ops.object.select_all(action='DESELECT') objects = collection_collision_export.all_objects for object in objects: object.select_set(True) bpy.ops.object.delete() bpy.data.collections.remove(collection_collision_export) def CombineExport(exportFolder): return #TBD classes = ( VIEW3D_PT_PanelExportAll, OBJECT_OT_SeparateExport, OBJECT_OT_CombineExport, UEExporterSettings ) def apply_modifiers(obj): ctx = bpy.context.copy() ctx['object'] = obj for _, m in enumerate(obj.modifiers): try: ctx['modifier'] = m bpy.ops.object.modifier_apply(ctx, modifier=m.name) except RuntimeError: print(f"Error applying {m.name} to {obj.name}, removing it instead.") obj.modifiers.remove(m) for m in obj.modifiers: obj.modifiers.remove(m) def register(): for c in classes: bpy.utils.register_class(c) bpy.types.Scene.ue_exporter = bpy.props.PointerProperty(type=UEExporterSettings) def unregister(): for c in reversed(classes): bpy.utils.unregister_class(c) del bpy.types.Scene.ue_exporter if __name__ == "__main__": register()