]> git.tdb.fi Git - libs/gl.git/commitdiff
Redesign object exporting
authorMikko Rasa <tdb@tdb.fi>
Thu, 15 Apr 2021 17:04:26 +0000 (20:04 +0300)
committerMikko Rasa <tdb@tdb.fi>
Thu, 15 Apr 2021 21:31:36 +0000 (00:31 +0300)
To combat operator bloat, all object-based data is now exported with a
common front-end class.  SceneExporter also uses this to export its
resources.

Meshes can't be exported on their own anymore, only as part of objects.
But loose meshes aren't that useful anymore with the improvements in
defining materials and techniques in Blender.

blender/io_mspgl/__init__.py
blender/io_mspgl/export.py [new file with mode: 0644]
blender/io_mspgl/export_armature.py
blender/io_mspgl/export_camera.py
blender/io_mspgl/export_mesh.py
blender/io_mspgl/export_object.py
blender/io_mspgl/export_scene.py

index 473750a53441ec3abb591d374e25b80c16f830e3..8de91d25a7ddb91b795c9c53daed4387df9a41db 100644 (file)
@@ -8,10 +8,11 @@ bl_info = {
 
 if "bpy" in locals():
        import imp
-       for sub in "animation", "armature", "datafile", "export_animation", "export_armature", "export_camera", "export_material", "export_mesh", "export_object", "export_scene", "export_texture", "material", "mesh", "properties", "util":
+       for sub in "animation", "armature", "datafile", "export", "export_animation", "export_armature", "export_camera", "export_material", "export_mesh", "export_object", "export_scene", "export_texture", "material", "mesh", "properties", "util":
                if sub in locals():
                        imp.reload(locals()[sub])
 
+import os
 import bpy
 from bpy_extras.io_utils import ExportHelper
 
@@ -39,66 +40,42 @@ class ExportMspGLBase(ExportHelper):
                for k, v in self.as_keywords().items():
                        setattr(exporter, k, v)
 
-class ExportMspGLMeshBase(ExportMspGLBase):
-       export_all: bpy.props.BoolProperty(name="Export all selected", description="Export all selected objects (use generated filenames)", default=False)
+class ExportMspGLData(bpy.types.Operator):
+       bl_idname = "export.mspgl_data"
+       bl_label = "Export Msp GL data"
+       bl_description = "Export object data in Msp GL format"
 
-       def draw(self, context):
-               self.general_col = self.layout.column()
-
-               col = self.layout.column()
-               if len(context.selected_objects)>1:
-                       col.label(text="Object selection")
-                       col.prop(self, "export_all")
-
-class ExportMspGLMesh(bpy.types.Operator, ExportMspGLMeshBase):
-       bl_idname = "export_mesh.mspgl_mesh"
-       bl_label = "Export Msp GL mesh"
-       bl_description = "Export one or more meshes in Msp GL format"
-
-       filename_ext = ".mesh"
-
-       def create_exporter(self):
-               from .export_mesh import MeshExporter
-               return MeshExporter()
-
-class ExportMspGLObject(bpy.types.Operator, ExportMspGLMeshBase):
-       bl_idname = "export_mesh.mspgl_object"
-       bl_label = "Export Msp GL object"
-       bl_description = "Export one or more objects in Msp GL format"
-
-       filename_ext = ".object"
-
-       collection: bpy.props.BoolProperty(name="As a collection", description="Write all data into a single collection file", default=False)
+       filepath: bpy.props.StringProperty(name="File path", description="File path for exporting the data", subtype='FILE_PATH')
+       collection: bpy.props.BoolProperty(name="As a collection", description="Export all data as a single collection file", default=False)
        shared_resources: bpy.props.BoolProperty(name="Shared resources", description="Use global names for resource files to enable sharing", default=True)
 
-       def check(self, context):
-               ext_changed = self.set_extension(".mdc" if self.collection else ".object")
-               super_result = super().check(context)
-               return ext_changed or super_result
+       @classmethod
+       def poll(cls, context):
+               return len(context.selected_objects)>0
 
-       def create_exporter(self):
-               from .export_object import ObjectExporter
-               return ObjectExporter()
+       def invoke(self, context, event):
+               blend_filepath = context.blend_data.filepath
+               if blend_filepath:
+                       self.filepath = os.path.splitext(blend_filepath)[0]+".mdc"
+               else:
+                       self.filepath = "data.mdc"
+               context.window_manager.fileselect_add(self)
+               return {'RUNNING_MODAL'}
 
-       def draw(self, context):
-               super().draw(context)
+       def execute(self, context):
+               from .export import DataExporter
+               exporter = DataExporter()
+               exporter.collection = self.collection
+               exporter.shared_resources = self.shared_resources
+               exporter.export_to_file(context, self.filepath)
+               return {'FINISHED'}
 
+       def draw(self, context):
                col = self.layout.column()
                col.label(text="Files")
                col.prop(self, "collection")
                col.prop(self, "shared_resources")
 
-class ExportMspGLArmature(bpy.types.Operator, ExportMspGLBase):
-       bl_idname = "export.mspgl_armature"
-       bl_label = "Export Msp GL armature"
-       bl_description = "Export an armature in Msp GL format"
-
-       filename_ext = ".arma"
-
-       def create_exporter(self):
-               from .export_armature import ArmatureExporter
-               return ArmatureExporter()
-
 class ExportMspGLAnimation(bpy.types.Operator, ExportMspGLBase):
        bl_idname = "export.mspgl_animation"
        bl_label = "Export Msp GL animation"
@@ -150,17 +127,6 @@ class ExportMspGLScene(bpy.types.Operator, ExportMspGLBase):
                if self.resource_collection:
                        col.prop(self, "skip_existing")
 
-class ExportMspGLCamera(bpy.types.Operator, ExportMspGLBase):
-       bl_idname = "export.mspgl_camera"
-       bl_label = "Export Msp GL camera"
-       bl_description = "Export a camera in Msp GL format"
-
-       filename_ext = ".camera"
-
-       def create_exporter(self):
-               from .export_camera import CameraExporter
-               return CameraExporter()
-
 class AddUniform(bpy.types.Operator):
        bl_idname = "material.add_uniform"
        bl_label = "Add Uniform"
@@ -186,14 +152,11 @@ class RemoveUniform(bpy.types.Operator):
                return {"FINISHED"}
 
 def menu_func_export(self, context):
-       self.layout.operator(ExportMspGLMesh.bl_idname, text="Msp GL mesh")
-       self.layout.operator(ExportMspGLObject.bl_idname, text="Msp GL object")
-       self.layout.operator(ExportMspGLArmature.bl_idname, text="Msp GL armature")
+       self.layout.operator(ExportMspGLData.bl_idname, text="Msp GL data")
        self.layout.operator(ExportMspGLAnimation.bl_idname, text="Msp GL animation")
        self.layout.operator(ExportMspGLScene.bl_idname, text="Msp GL scene")
-       self.layout.operator(ExportMspGLCamera.bl_idname, text="Msp GL camera")
 
-classes = [ExportMspGLMesh, ExportMspGLObject, ExportMspGLArmature, ExportMspGLAnimation, ExportMspGLScene, ExportMspGLCamera, AddUniform, RemoveUniform]
+classes = [ExportMspGLData, ExportMspGLAnimation, ExportMspGLScene, AddUniform, RemoveUniform]
 
 def register():
        for c in classes:
diff --git a/blender/io_mspgl/export.py b/blender/io_mspgl/export.py
new file mode 100644 (file)
index 0000000..96f3932
--- /dev/null
@@ -0,0 +1,78 @@
+import os
+
+class DataExporter:
+       def __init__(self):
+               self.show_progress = True
+               self.collection = False
+               self.shared_resources = True
+
+       def export_to_file(self, context, out_fn):
+               from .util import Progress
+               progress = Progress(self.show_progress and context)
+
+               objects = context.selected_objects
+
+               resources = {}
+               material_atlases = {}
+
+               dummy_res = self.export_resources(context, objects, resources, material_atlases, progress)
+
+               path, base = os.path.split(out_fn)
+               base, ext = os.path.splitext(base)
+
+               refs = dummy_res.collect_references()
+               if not self.shared_resources:
+                       numbers = {}
+                       for r in refs:
+                               res_ext = os.path.splitext(r.name)[1]
+                               n = numbers.get(res_ext, 0)
+                               if n>0:
+                                       r.name = "{}_{}{}".format(base, n, res_ext)
+                               else:
+                                       r.name = base+res_ext
+                               numbers[res_ext] = n+1
+
+               if self.collection:
+                       dummy_res.write_collection(out_fn, exclude_self=True)
+               else:
+                       for r in refs:
+                               r.write_to_file(os.path.join(path, r.name))
+
+       def export_resources(self, context, objects, resources, material_atlases, progress):
+               if material_atlases is None:
+                       material_atlases = {}
+
+               object_exporter = None
+               camera_exporter = None
+               armature_exporter = None
+
+               from .datafile import Resource
+               dummy_res = Resource("dummy", "dummy")
+
+               for i, obj in enumerate(objects):
+                       progress.push_task_slice(obj.name, i, len(objects))
+                       res = None
+                       if obj.type=='MESH':
+                               if not object_exporter:
+                                       from .export_object import ObjectExporter
+                                       object_exporter = ObjectExporter()
+                               object_exporter.export_object_resources(context, obj, resources, material_atlases, progress)
+                               res = object_exporter.export_object(obj, resources, progress)
+                       elif obj.type=='CAMERA':
+                               if not camera_exporter:
+                                       from .export_camera import CameraExporter
+                                       camera_exporter = CameraExporter()
+                               res = camera_exporter.export_camera(obj)
+                       elif obj.type=='ARMATURE':
+                               if not armature_exporter:
+                                       from .export_armature import ArmatureExporter
+                                       armature_exporter = ArmatureExporter()
+                               res = armature_exporter.export_armature(context, obj)
+
+                       if res:
+                               resources[res.name] = res
+                               dummy_res.create_reference_statement("ref", res)
+
+                       progress.pop_task()
+
+               return dummy_res
index c2ec67125f64c93f49322a5bbbb3e71bde68e485..27bf6e0b05df9341369258b0daefa4aa8d1dad0e 100644 (file)
@@ -1,16 +1,7 @@
 class ArmatureExporter:
-       def export_to_file(self, context, out_fn):
-               obj = context.active_object
-
-               statements = self.export_armature(context, obj)
-
-               with open(out_fn, "w") as out_file:
-                       for s in statements:
-                               s.write_to_file(out_file)
-
        def export_armature(self, obj):
                if obj.type!="ARMATURE":
-                       raise Exception("Object is not an armature")
+                       raise ValueError("Object is not an armature")
 
                from .armature import Armature
                armature = Armature(obj.data)
index fb45535b199c77f600b8bb43e758570264ffb3ab..02fce103c7f13d1f7fdc7e9fd934bd512c2f9031 100644 (file)
@@ -2,16 +2,9 @@ import math
 import mathutils
 
 class CameraExporter:
-       def export_to_file(self, context, out_fn):
-               obj = context.active_object
-
-               resource = self.export_camera(obj)
-
-               resource.write_to_file(out_fn)
-
        def export_camera(self, obj):
                if obj.type!='CAMERA':
-                       raise Exception("Object is not a camera")
+                       raise ValueError("Object is not a camera")
 
                from .datafile import Resource, Statement
                resource = Resource(obj.name+".camera", "camera")
index 24226f2972512d8758aeda1c65fd37482c35faeb..2a8dbe0057062411541507d1faf6fad0509e9812 100644 (file)
@@ -4,32 +4,6 @@ import bpy
 import mathutils
 
 class MeshExporter:
-       def __init__(self):
-               self.show_progress = True
-               self.export_all = False
-
-       def export_to_file(self, context, out_fn):
-               if self.export_all:
-                       objs = [o for o in context.selected_objects if o.type=="MESH"]
-               else:
-                       objs = [context.active_object]
-
-               from .util import Progress
-
-               path, base = os.path.split(out_fn)
-               base, ext = os.path.splitext(base)
-
-               progress = Progress(self.show_progress and context)
-               for i, obj in enumerate(objs):
-                       if self.export_all:
-                               out_fn = os.path.join(path, obj.data.name+ext)
-
-                       progress.push_task_slice(obj.data.name, i, len(objs))
-                       resource = self.export_mesh(context, obj, progress)
-
-                       resource.write_to_file(out_fn)
-                       progress.pop_task()
-
        def export_mesh(self, context, mesh_or_obj, progress):
                from .mesh import Mesh, create_mesh_from_object
 
index 54bde96a5d29962f2ad9bac4279d0fe77d67a2ce..3ee06fc218c3fe1a618826eb22668b4687739e26 100644 (file)
@@ -2,12 +2,6 @@ import os
 import mathutils
 
 class ObjectExporter:
-       def __init__(self):
-               self.show_progress = True
-               self.export_all = False
-               self.collection = False
-               self.shared_resources = True
-
        def compute_bounding_sphere(self, obj):
                p1 = max(((v.co, v.co.length) for v in obj.data.vertices), key=lambda x:x[1])[0]
                p2 = max(((v.co, (v.co-p1).length) for v in obj.data.vertices), key=lambda x:x[1])[0]
@@ -45,48 +39,6 @@ class ObjectExporter:
                material_atlas_export = MaterialAtlasExporter()
                return material_atlas_export
 
-       def export_to_file(self, context, out_fn):
-               if self.export_all:
-                       objs = [o for o in context.selected_objects if o.type=="MESH"]
-               else:
-                       objs = [context.active_object]
-
-               from .util import Progress
-               progress = Progress(self.show_progress and context)
-
-               path, base = os.path.split(out_fn)
-               base, ext = os.path.splitext(base)
-
-               resources = {}
-               for i, obj in enumerate(objs):
-                       if self.export_all:
-                               out_fn = os.path.join(path, obj.name+ext)
-
-                       progress.push_task_slice(obj.name, i, len(objs))
-                       self.export_object_resources(context, obj, resources, None, progress)
-
-                       obj_res = self.export_object(context, obj, resources, progress)
-                       refs = obj_res.collect_references()
-                       if not self.shared_resources:
-                               numbers = {}
-                               for r in refs:
-                                       res_ext = os.path.splitext(r.name)[1]
-                                       n = numbers.get(res_ext, 0)
-                                       if n>0:
-                                               r.name = "{}_{}{}".format(base, n, res_ext)
-                                       else:
-                                               r.name = base+res_ext
-                                       numbers[res_ext] = n+1
-
-                       if self.collection:
-                               obj_res.write_collection(out_fn)
-                       else:
-                               for r in refs:
-                                       r.write_to_file(os.path.join(path, r.name))
-                               obj_res.write_to_file(out_fn)
-
-                       progress.pop_task()
-
        def export_object_resources(self, context, obj, resources, material_atlases, progress):
                if material_atlases is None:
                        material_atlases = {}
@@ -140,10 +92,9 @@ class ObjectExporter:
 
                        progress.pop_task()
 
-       def export_object(self, context, obj, resources, progress):
-               if resources is None:
-                       resources = {}
-                       self.export_object_resources(context, obj, resources, None, progress)
+       def export_object(self, obj, resources, progress):
+               if obj.type!='MESH':
+                       raise ValueError("Object is not a mesh")
 
                lods = self.collect_object_lods(obj)
 
index b2c3a2e671b6f12c8ded80ee8a1dbee098b9d7ed..1dea4b2416b5a7c0d46952570b19448d4be930e9 100644 (file)
@@ -17,16 +17,13 @@ class SceneExporter:
                if self.visible_collections:
                        collections = [c.collection for c in context.view_layer.layer_collection.children if not (c.hide_viewport or c.collection.hide_viewport)]
                        objs = [o for o in objs if any((o.name in c.all_objects) for c in collections)]
-               objs = [o for o in objs if o.type=="MESH" and not o.lod_for_parent]
+               objs = [o for o in objs if o.type=="MESH" and not o.lod_for_parent and o.data.vertices]
                objs = [o for o in objs if (not o.compound or o.parent not in objs)]
                objs.sort(key=lambda x:x.name)
 
                path, base = os.path.split(out_fn)
                base, ext = os.path.splitext(base)
 
-               from .export_object import ObjectExporter
-               object_export = ObjectExporter()
-
                object_prototypes = {}
                unique_objects = []
                export_names = {}
@@ -75,8 +72,11 @@ class SceneExporter:
                from .util import Progress
                progress = Progress(self.show_progress and context)
 
+               from .export import DataExporter
+               data_exporter = DataExporter()
+
                resources = {}
-               self.export_scene_resources(context, unique_objects, resources, progress)
+               data_exporter.export_resources(context, unique_objects, resources, None, progress)
                for n, r in resources.items():
                        if r.name in export_names:
                                r.name = export_names[r.name]
@@ -98,20 +98,6 @@ class SceneExporter:
 
                scene_res.write_to_file(out_fn)
 
-       def export_scene_resources(self, context, objs, resources, progress):
-               from .export_object import ObjectExporter
-               object_export = ObjectExporter()
-               object_export.single_file = False
-
-               material_atlases = {}
-
-               for i, o in enumerate(objs):
-                       progress.push_task_slice(o.name, i, len(objs))
-                       object_export.export_object_resources(context, o, resources, material_atlases, progress)
-                       obj_name = o.name+".object"
-                       resources[obj_name] = object_export.export_object(context, o, resources, progress)
-                       progress.pop_task()
-
        def export_scene(self, context, objs, resources, prototypes, progress):
                from .datafile import Resource, Statement, Token
                scene_res = Resource("scene.scene", "scene")