bl_info = { "name": "Prism Asset Pipeline", "blender": (2, 80, 0), "location": "File > Import-Export", "category": "Import-Export" } import bpy from bpy.types import Operator, AddonPreferences from bpy.props import StringProperty, BoolProperty, PointerProperty from bpy_extras.io_utils import ( ExportHelper, ) import subprocess class MarkExportOperator(bpy.types.Operator): bl_idname = "scene.mark_export_operator" bl_label = "Mark All Objects for Export" def execute(self, context): for ob in context.scene.objects: if ob.type == 'MESH': ob.willExport = True return {'FINISHED'} class HelloWorldPanel(bpy.types.Panel): bl_idname = "OBJECT_PT_hello_world" bl_label = "Prism Asset Pipeline" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "object" def draw(self, context): self.layout.prop(context.object, "modelContainer", text="Model Group") self.layout.prop(context.object, "willExport", text="Will Export") self.layout.prop(context.object, "exportArmature", text="Export Armature") self.layout.prop(context.object, "exportAnim", text="Export Animations") class ExportSettingsPanel(bpy.types.Panel): bl_idname = "OBJECT_PT_prism_srttings" bl_label = "Prism Asset Pipeline" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "scene" def draw(self, context): self.layout.operator("scene.mark_export_operator") self.layout.prop(context.scene, "export_prefix", text="Export Prefix") self.layout.prop(context.scene, "removeTransforms", text="Remove Transforms") def console_get(): for area in bpy.context.screen.areas: if area.type == 'CONSOLE': for space in area.spaces: if space.type == 'CONSOLE': return area, space return None, None def console_write(text): area, space = console_get() if space is None: return context = bpy.context.copy() context.update(dict( space=space, area=area, )) for line in text.split("\n"): bpy.ops.console.scrollback_append(context, text=line, type='OUTPUT') class ExampleAddonPreferences(AddonPreferences): bl_idname = __name__ def update_func(self, context): if "//" in self.modelCompilerPath: self.modelCompilerPath = bpy.path.abspath(self.modelCompilerPath) if "//" in self.dataExportPath: self.dataExportPath = bpy.path.abspath(self.dataExportPath) modelCompilerPath = StringProperty( name="Model Compiler Path", subtype='FILE_PATH', update=update_func ) dataExportPath = StringProperty( name="Data Export Path", subtype='DIR_PATH', update=update_func ) def draw(self, context): layout = self.layout layout.prop(self, "modelCompilerPath") layout.prop(self, "dataExportPath") class ExportAssetPipeline(bpy.types.Operator): bl_idname = "prism.export_pipeline" bl_label = "Export Prism Engine" action = bpy.props.StringProperty(default="EVERYTHING") def execute(self, context): preferences = context.preferences addon_prefs = preferences.addons[__name__].preferences if len(addon_prefs.modelCompilerPath) == 0 or len(addon_prefs.dataExportPath) == 0: console_write("No modelS compiler or export path!") return {'FINISHED'} model_compiler = bpy.path.abspath(addon_prefs.modelCompilerPath) export_groups = {} for object in context.scene.objects: if object.willExport: if not object.modelContainer in export_groups: export_groups[object.modelContainer] = {} export_groups[object.modelContainer]["objects"] = [] console_write("exporting " + object.name + " as " + object.modelContainer) export_groups[object.modelContainer]["objects"].append(object.name) if object.exportArmature is not None: export_groups[object.modelContainer]["armature"] = object.exportArmature.name console_write(str(export_groups)) for group in export_groups.keys(): bpy.ops.object.select_all(action='DESELECT') will_export_anim = False if "armature" in export_groups[group]: object = context.scene.objects[export_groups[group]["armature"]] object.select_set(state=True) for object_name in export_groups[group]["objects"]: object = context.scene.objects[object_name] if object.exportAnim == True: will_export_anim = True object.select_set(state=True) name_part = context.scene.export_prefix + group fbx_path = "//" + name_part + ".fbx" fbx_path = bpy.path.abspath(fbx_path) if self.action == 'ONLY_MESH': will_export_anim = False options_dict = {'filepath': fbx_path, 'check_existing': False, 'use_selection': True, 'bake_anim': will_export_anim, 'apply_unit_scale': True, 'add_leaf_bones': False, 'use_armature_deform_only': True, 'mesh_smooth_type': 'OFF', 'bake_space_transform': context.scene.removeTransforms, 'use_tspace': True } if context.scene.removeTransforms: #options_dict['bake_space_transform'] =True bpy.ops.export_scene.fbx(**options_dict) else: bpy.ops.export_scene.fbx(**options_dict) args = [model_compiler, "--no_ui", "--model-path", fbx_path, "--data-path", bpy.path.abspath(addon_prefs.dataExportPath)] if context.scene.removeTransforms: args.append("--compile-static") p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) output, errors = p.communicate() return {'FINISHED'} class PrismExportSubMenu(bpy.types.Menu): bl_idname = "OBJECT_MT_prism_export_submenu" bl_label = "Prism Asset Pipeline" def draw(self, context): self.layout.operator(ExportAssetPipeline.bl_idname, text="Everything").action = 'EVERYTHING' self.layout.operator(ExportAssetPipeline.bl_idname, text="Only Meshes").action = 'ONLY_MESH' def menu_func_export(self, context): self.layout.menu(PrismExportSubMenu.bl_idname) def register(): bpy.utils.register_class(HelloWorldPanel) bpy.utils.register_class(ExportSettingsPanel) bpy.utils.register_class(ExportAssetPipeline) bpy.utils.register_class(MarkExportOperator) bpy.utils.register_class(PrismExportSubMenu) # properties bpy.types.Object.modelContainer = StringProperty() bpy.types.Object.willExport = BoolProperty() bpy.types.Object.exportAnim = BoolProperty() bpy.types.Scene.export_prefix = StringProperty() bpy.types.Scene.removeTransforms = BoolProperty() bpy.types.Object.exportArmature = PointerProperty(type=bpy.types.Object) bpy.types.TOPBAR_MT_file_export.append(menu_func_export) bpy.utils.register_class(ExampleAddonPreferences) def unregister(): bpy.utils.unregister_class(HelloWorldPanel) bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) bpy.utils.unregister_class(ExportAssetPipeline) bpy.utils.unregister_class(ExampleAddonPreferences) bpy.utils.unregister_class(MarkExportOperator) if __name__ == "__main__": register()