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:
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user