Skip to content

Commit

Permalink
Added handling for collection exports
Browse files Browse the repository at this point in the history
Also changed `Use Raw Data` to an `Object Evaluation Mode` enum to
better match the terminology used in other parts of Blender.
  • Loading branch information
cmbasnett committed Jul 18, 2024
1 parent cfc533b commit c8ca818
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 53 deletions.
27 changes: 14 additions & 13 deletions io_scene_ase/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ASEBuilderError(Exception):

class ASEBuilderOptions(object):
def __init__(self):
self.use_raw_mesh_data = False
self.object_eval_state = 'EVALUATED'
self.materials: Optional[List[Material]] = None


Expand Down Expand Up @@ -55,18 +55,19 @@ def build(self, context: Context, options: ASEBuilderOptions, objects: Iterable[
matrix_world = get_object_matrix(obj, asset_instance)

# Evaluate the mesh after modifiers are applied
if options.use_raw_mesh_data:
mesh_object = obj
mesh_data = mesh_object.data
else:
depsgraph = context.evaluated_depsgraph_get()
bm = bmesh.new()
bm.from_object(obj, depsgraph)
mesh_data = bpy.data.meshes.new('')
bm.to_mesh(mesh_data)
del bm
mesh_object = bpy.data.objects.new('', mesh_data)
mesh_object.matrix_world = matrix_world
match options.object_eval_state:
case 'ORIGINAL':
mesh_object = obj
mesh_data = mesh_object.data
case 'EVALUATED':
depsgraph = context.evaluated_depsgraph_get()
bm = bmesh.new()
bm.from_object(obj, depsgraph)
mesh_data = bpy.data.meshes.new('')
bm.to_mesh(mesh_data)
del bm
mesh_object = bpy.data.objects.new('', mesh_data)
mesh_object.matrix_world = matrix_world

if not is_collision_name(obj.name) and main_geometry_object is not None:
geometry_object = main_geometry_object
Expand Down
102 changes: 63 additions & 39 deletions io_scene_ase/exporter.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import os.path
from typing import Iterable, List, Set, Union

import bpy
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, CollectionProperty, PointerProperty, IntProperty
from bpy.types import Operator, Material, PropertyGroup, UIList, Object
from bpy.props import StringProperty, BoolProperty, CollectionProperty, PointerProperty, IntProperty, EnumProperty
from bpy.types import Operator, Material, PropertyGroup, UIList, Object, FileHandler, Collection
from .builder import ASEBuilder, ASEBuilderOptions, ASEBuilderError, get_mesh_objects
from .writer import ASEWriter

Expand Down Expand Up @@ -79,6 +80,11 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn
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):
bl_idname = 'io_scene_ase.ase_export'
Expand All @@ -88,7 +94,11 @@ class ASE_OT_export(Operator, ExportHelper):
bl_description = 'Export selected objects to ASE'
filename_ext = '.ase'
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):
layout = self.layout
Expand All @@ -107,7 +117,9 @@ def draw(self, context):
advanced_header.label(text='Advanced')

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]]:
mesh_objects = [x[0] for x in get_mesh_objects(context.selected_objects)]
Expand All @@ -121,7 +133,7 @@ def invoke(self, context: 'Context', event: 'Event' ) -> Union[Set[str], Set[int

def execute(self, context):
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')
options.materials = [x.material for x in pg.material_list]
try:
Expand All @@ -134,67 +146,79 @@ def execute(self, context):
return {'CANCELLED'}


class ASE_OT_export_collections(Operator, ExportHelper):
bl_idname = 'io_scene_ase.ase_export_collections'
bl_label = 'Export Collections to ASE'
class ASE_OT_export_collection(Operator, ExportHelper):
bl_idname = 'io_scene_ase.ase_export_collection'
bl_label = 'Export collection to ASE'
bl_space_type = 'PROPERTIES'
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'
filter_glob: StringProperty(
default="*.ase",
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(
default=False,
description='No modifiers will be evaluated as part of the exported mesh',
name='Raw Mesh Data')
object_eval_state: EnumProperty(
items=object_eval_state_items,
name='Data',
default='EVALUATED'
)

collection: StringProperty()


def draw(self, context):
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):
options = ASEBuilderOptions()
options.use_raw_mesh_data = self.use_raw_mesh_data
collection = bpy.data.collections.get(self.collection)

# 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]
options = ASEBuilderOptions()
options.object_eval_state = self.object_eval_state

context.window_manager.progress_begin(0, len(layer_collections))
# Iterate over all the objects in the collection.
mesh_objects = get_mesh_objects(collection.all_objects)
# Get all the materials used by the objects in the collection.
options.materials = get_unique_materials([x[0] for x in mesh_objects])

for i, collection in enumerate(collections):
# Iterate over all the objects in the collection.
mesh_objects = get_mesh_objects(collection.all_objects)
# Get all the materials used by the objects in the collection.
options.materials = get_unique_materials([x[0] for x in mesh_objects])
try:
ase = ASEBuilder().build(context, options, collection.all_objects)
except ASEBuilderError as e:
self.report({'ERROR'}, str(e))
return {'CANCELLED'}

try:
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:
self.report({'ERROR'}, str(e))
return {'CANCELLED'}
try:
ASEWriter().write(self.filepath, ase)
except PermissionError as e:
self.report({'ERROR'}, 'ASCII Scene Export: ' + str(e))
return {'CANCELLED'}

context.window_manager.progress_update(i)
return {'FINISHED'}

context.window_manager.progress_end()

self.report({'INFO'}, f'{len(collections)} collections exported successfully')
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'

return {'FINISHED'}


classes = (
ASE_PG_material,
ASE_UL_materials,
ASE_PG_export,
ASE_OT_export,
ASE_OT_export_collections,
ASE_OT_export_collection,
ASE_OT_material_list_move_down,
ASE_OT_material_list_move_up,
ASE_FH_export,
)
2 changes: 1 addition & 1 deletion io_scene_ase/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,6 @@ def build_ase_tree(ase) -> ASEFile:

def write(self, filepath, ase):
self.indent = 0
ase_file = self.build_ase_tree(ase)
with open(filepath, 'w') as self.fp:
ase_file = self.build_ase_tree(ase)
self.write_file(ase_file)

0 comments on commit c8ca818

Please sign in to comment.