Added handling for collection exports

Also changed `Use Raw Data` to an `Object Evaluation Mode` enum to
better match the terminology used in other parts of Blender.
This commit is contained in:
Colin Basnett
2024-07-18 11:19:57 -07:00
parent cfc533b2ad
commit c8ca818e43
3 changed files with 80 additions and 55 deletions

View File

@@ -16,7 +16,7 @@ class ASEBuilderError(Exception):
class ASEBuilderOptions(object): class ASEBuilderOptions(object):
def __init__(self): def __init__(self):
self.use_raw_mesh_data = False self.object_eval_state = 'EVALUATED'
self.materials: Optional[List[Material]] = None self.materials: Optional[List[Material]] = None
@@ -55,10 +55,11 @@ class ASEBuilder(object):
matrix_world = get_object_matrix(obj, asset_instance) matrix_world = get_object_matrix(obj, asset_instance)
# Evaluate the mesh after modifiers are applied # Evaluate the mesh after modifiers are applied
if options.use_raw_mesh_data: match options.object_eval_state:
case 'ORIGINAL':
mesh_object = obj mesh_object = obj
mesh_data = mesh_object.data mesh_data = mesh_object.data
else: case 'EVALUATED':
depsgraph = context.evaluated_depsgraph_get() depsgraph = context.evaluated_depsgraph_get()
bm = bmesh.new() bm = bmesh.new()
bm.from_object(obj, depsgraph) bm.from_object(obj, depsgraph)

View File

@@ -1,9 +1,10 @@
import os.path import os.path
from typing import Iterable, List, Set, Union from typing import Iterable, List, Set, Union
import bpy
from bpy_extras.io_utils import ExportHelper from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, CollectionProperty, PointerProperty, IntProperty from bpy.props import StringProperty, BoolProperty, CollectionProperty, PointerProperty, IntProperty, EnumProperty
from bpy.types import Operator, Material, PropertyGroup, UIList, Object from bpy.types import Operator, Material, PropertyGroup, UIList, Object, FileHandler, Collection
from .builder import ASEBuilder, ASEBuilderOptions, ASEBuilderError, get_mesh_objects from .builder import ASEBuilder, ASEBuilderOptions, ASEBuilderError, get_mesh_objects
from .writer import ASEWriter from .writer import ASEWriter
@@ -79,6 +80,11 @@ class ASE_UL_materials(UIList):
row.prop(item.material, 'name', text='', emboss=False, icon_value=layout.icon(item.material)) row.prop(item.material, 'name', text='', emboss=False, icon_value=layout.icon(item.material))
object_eval_state_items = (
('EVALUATED', 'Evaluated', 'Use data from fully evaluated object'),
('ORIGINAL', 'Original', 'Use data from original object with no modifiers applied'),
)
class ASE_OT_export(Operator, ExportHelper): class ASE_OT_export(Operator, ExportHelper):
bl_idname = 'io_scene_ase.ase_export' bl_idname = 'io_scene_ase.ase_export'
@@ -88,7 +94,11 @@ class ASE_OT_export(Operator, ExportHelper):
bl_description = 'Export selected objects to ASE' bl_description = 'Export selected objects to ASE'
filename_ext = '.ase' filename_ext = '.ase'
filter_glob: StringProperty(default="*.ase", options={'HIDDEN'}, maxlen=255) filter_glob: StringProperty(default="*.ase", options={'HIDDEN'}, maxlen=255)
use_raw_mesh_data: BoolProperty(default=False, name='Raw Mesh Data', description='No modifiers will be evaluated as part of the exported mesh') object_eval_state: EnumProperty(
items=object_eval_state_items,
name='Data',
default='EVALUATED'
)
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
@@ -107,7 +117,9 @@ class ASE_OT_export(Operator, ExportHelper):
advanced_header.label(text='Advanced') advanced_header.label(text='Advanced')
if advanced_panel: if advanced_panel:
advanced_panel.prop(self, 'use_raw_mesh_data') advanced_panel.use_property_split = True
advanced_panel.use_property_decorate = False
advanced_panel.prop(self, 'object_eval_state')
def invoke(self, context: 'Context', event: 'Event' ) -> Union[Set[str], Set[int]]: def invoke(self, context: 'Context', event: 'Event' ) -> Union[Set[str], Set[int]]:
mesh_objects = [x[0] for x in get_mesh_objects(context.selected_objects)] mesh_objects = [x[0] for x in get_mesh_objects(context.selected_objects)]
@@ -121,7 +133,7 @@ class ASE_OT_export(Operator, ExportHelper):
def execute(self, context): def execute(self, context):
options = ASEBuilderOptions() options = ASEBuilderOptions()
options.use_raw_mesh_data = self.use_raw_mesh_data options.object_eval_state = self.object_eval_state
pg = getattr(context.scene, 'ase_export') pg = getattr(context.scene, 'ase_export')
options.materials = [x.material for x in pg.material_list] options.materials = [x.material for x in pg.material_list]
try: try:
@@ -134,38 +146,44 @@ class ASE_OT_export(Operator, ExportHelper):
return {'CANCELLED'} return {'CANCELLED'}
class ASE_OT_export_collections(Operator, ExportHelper): class ASE_OT_export_collection(Operator, ExportHelper):
bl_idname = 'io_scene_ase.ase_export_collections' bl_idname = 'io_scene_ase.ase_export_collection'
bl_label = 'Export Collections to ASE' bl_label = 'Export collection to ASE'
bl_space_type = 'PROPERTIES' bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW' bl_region_type = 'WINDOW'
bl_description = 'Batch export collections to ASE. The name of the collection will be used as the filename' bl_description = 'Export collection to ASE'
filename_ext = '.ase' filename_ext = '.ase'
filter_glob: StringProperty( filter_glob: StringProperty(
default="*.ase", default="*.ase",
options={'HIDDEN'}, options={'HIDDEN'},
maxlen=255, # Max internal buffer length, longer would be hilighted. maxlen=255, # Max internal buffer length, longer would be highlighted.
) )
use_raw_mesh_data: BoolProperty( object_eval_state: EnumProperty(
default=False, items=object_eval_state_items,
description='No modifiers will be evaluated as part of the exported mesh', name='Data',
name='Raw Mesh Data') default='EVALUATED'
)
collection: StringProperty()
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.prop(self, 'use_raw_mesh_data')
advanced_header, advanced_panel = layout.panel('Advanced', default_closed=True)
advanced_header.label(text='Advanced')
if advanced_panel:
advanced_panel.use_property_split = True
advanced_panel.use_property_decorate = False
advanced_panel.prop(self, 'object_eval_state')
def execute(self, context): def execute(self, context):
collection = bpy.data.collections.get(self.collection)
options = ASEBuilderOptions() options = ASEBuilderOptions()
options.use_raw_mesh_data = self.use_raw_mesh_data options.object_eval_state = self.object_eval_state
# Iterate over all the visible collections in the scene.
layer_collections = context.view_layer.layer_collection.children
collections = [x.collection for x in layer_collections if not x.hide_viewport and not x.exclude]
context.window_manager.progress_begin(0, len(layer_collections))
for i, collection in enumerate(collections):
# Iterate over all the objects in the collection. # Iterate over all the objects in the collection.
mesh_objects = get_mesh_objects(collection.all_objects) mesh_objects = get_mesh_objects(collection.all_objects)
# Get all the materials used by the objects in the collection. # Get all the materials used by the objects in the collection.
@@ -173,28 +191,34 @@ class ASE_OT_export_collections(Operator, ExportHelper):
try: try:
ase = ASEBuilder().build(context, options, collection.all_objects) ase = ASEBuilder().build(context, options, collection.all_objects)
dirname = os.path.dirname(self.filepath)
filepath = os.path.join(dirname, collection.name + '.ase')
ASEWriter().write(filepath, ase)
except ASEBuilderError as e: except ASEBuilderError as e:
self.report({'ERROR'}, str(e)) self.report({'ERROR'}, str(e))
return {'CANCELLED'} return {'CANCELLED'}
context.window_manager.progress_update(i) try:
ASEWriter().write(self.filepath, ase)
context.window_manager.progress_end() except PermissionError as e:
self.report({'ERROR'}, 'ASCII Scene Export: ' + str(e))
self.report({'INFO'}, f'{len(collections)} collections exported successfully') return {'CANCELLED'}
return {'FINISHED'} return {'FINISHED'}
class ASE_FH_export(FileHandler):
bl_idname = 'ASE_FH_export'
bl_label = 'ASCII Scene Export'
bl_export_operator = ASE_OT_export_collection.bl_idname
bl_file_extensions = '.ase'
classes = ( classes = (
ASE_PG_material, ASE_PG_material,
ASE_UL_materials, ASE_UL_materials,
ASE_PG_export, ASE_PG_export,
ASE_OT_export, ASE_OT_export,
ASE_OT_export_collections, ASE_OT_export_collection,
ASE_OT_material_list_move_down, ASE_OT_material_list_move_down,
ASE_OT_material_list_move_up, ASE_OT_material_list_move_up,
ASE_FH_export,
) )

View File

@@ -191,6 +191,6 @@ class ASEWriter(object):
def write(self, filepath, ase): def write(self, filepath, ase):
self.indent = 0 self.indent = 0
ase_file = self.build_ase_tree(ase)
with open(filepath, 'w') as self.fp: with open(filepath, 'w') as self.fp:
ase_file = self.build_ase_tree(ase)
self.write_file(ase_file) self.write_file(ase_file)