Fix #104576: FBX export of Edit Mode Objects while Blender is not in Edit Mode can fail. #104633
@ -77,7 +77,7 @@ from .fbx_utils import (
|
||||
# Animation.
|
||||
AnimationCurveNodeWrapper,
|
||||
# Objects.
|
||||
ObjectWrapper, fbx_name_class,
|
||||
ObjectWrapper, fbx_name_class, ensure_object_not_in_edit_mode,
|
||||
# Top level.
|
||||
FBXExportSettingsMedia, FBXExportSettings, FBXExportData,
|
||||
)
|
||||
@ -3497,6 +3497,14 @@ def save(operator, context,
|
||||
ctx_objects = context.view_layer.objects
|
||||
if use_visible:
|
||||
ctx_objects = tuple(obj for obj in ctx_objects if obj.visible_get())
|
||||
|
||||
# Ensure no Objects are in Edit mode.
|
||||
# Copy to a tuple for safety, to avoid the risk of modifying ctx_objects while iterating.
|
||||
for obj in tuple(ctx_objects):
|
||||
if not ensure_object_not_in_edit_mode(context, obj):
|
||||
operator.report({'ERROR'}, "%s could not be set out of Edit Mode, so cannot be exported" % obj.name)
|
||||
return {'CANCELLED'}
|
||||
|
||||
kwargs_mod["context_objects"] = ctx_objects
|
||||
|
||||
depsgraph = context.evaluated_depsgraph_get()
|
||||
@ -3528,6 +3536,16 @@ def save(operator, context,
|
||||
else:
|
||||
data_seq = tuple((scene, scene.name, 'objects') for scene in bpy.data.scenes if scene.objects)
|
||||
|
||||
# Ensure no Objects are in Edit mode.
|
||||
for data, data_name, data_obj_propname in data_seq:
|
||||
# Copy to a tuple for safety, to avoid the risk of modifying the data prop while iterating it.
|
||||
for obj in tuple(getattr(data, data_obj_propname)):
|
||||
if not ensure_object_not_in_edit_mode(context, obj):
|
||||
operator.report({'ERROR'},
|
||||
"%s in %s could not be set out of Edit Mode, so cannot be exported"
|
||||
% (obj.name, data_name))
|
||||
return {'CANCELLED'}
|
||||
|
||||
# call this function within a loop with BATCH_ENABLE == False
|
||||
|
||||
new_fbxpath = fbxpath # own dir option modifies, we need to keep an original
|
||||
|
@ -540,6 +540,58 @@ def fast_first_axis_unique(ar, return_unique=True, return_index=False, return_in
|
||||
return result
|
||||
|
||||
|
||||
def ensure_object_not_in_edit_mode(context, obj):
|
||||
"""Objects in Edit mode usually cannot be exported because much of the API used when exporting is not available for
|
||||
Objects in Edit mode.
|
||||
|
||||
Exiting the currently active Object (and any other Objects opened in multi-editing) from Edit mode is simple and
|
||||
should be done with `bpy.ops.mesh.mode_set(mode='OBJECT')` instead of using this function.
|
||||
|
||||
This function is for the rare case where an Object is in Edit mode, but the current context mode is not Edit mode.
|
||||
This can occur from a state where the current context mode is Edit mode, but then the active Object of the current
|
||||
View Layer is changed to a different Object that is not in Edit mode. This changes the current context mode, but
|
||||
leaves the other Object(s) in Edit mode.
|
||||
"""
|
||||
if obj.mode != 'EDIT':
|
||||
return True
|
||||
|
||||
# Get the active View Layer.
|
||||
view_layer = context.view_layer
|
||||
|
||||
# A View Layer belongs to a scene.
|
||||
scene = view_layer.id_data
|
||||
|
||||
# Get the current active Object of this View Layer, so we can restore it once done.
|
||||
orig_active = view_layer.objects.active
|
||||
|
||||
# Check if obj is in the View Layer. If obj is not in the View Layer, it cannot be set as the active Object.
|
||||
# We don't use `obj.name in view_layer.objects` because an Object from a Library could have the same name.
|
||||
is_in_view_layer = any(o == obj for o in view_layer.objects)
|
||||
|
||||
do_unlink_from_scene_collection = False
|
||||
try:
|
||||
if not is_in_view_layer:
|
||||
# There might not be any enabled collections in the View Layer, so link obj into the Scene Collection
|
||||
# instead, which is always available to all View Layers of that Scene.
|
||||
scene.collection.objects.link(obj)
|
||||
do_unlink_from_scene_collection = True
|
||||
view_layer.objects.active = obj
|
||||
|
||||
# Now we're finally ready to attempt to change obj's mode.
|
||||
if bpy.ops.object.mode_set.poll():
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
if obj.mode == 'EDIT':
|
||||
# The Object could not be set out of EDIT mode and therefore cannot be exported.
|
||||
return False
|
||||
finally:
|
||||
# Always restore the original active Object and unlink obj from the Scene Collection if it had to be linked.
|
||||
view_layer.objects.active = orig_active
|
||||
if do_unlink_from_scene_collection:
|
||||
scene.collection.objects.unlink(obj)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# ##### UIDs code. #####
|
||||
|
||||
# ID class (mere int).
|
||||
|
Loading…
Reference in New Issue
Block a user