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.
|
# Animation.
|
||||||
AnimationCurveNodeWrapper,
|
AnimationCurveNodeWrapper,
|
||||||
# Objects.
|
# Objects.
|
||||||
ObjectWrapper, fbx_name_class,
|
ObjectWrapper, fbx_name_class, ensure_object_not_in_edit_mode,
|
||||||
# Top level.
|
# Top level.
|
||||||
FBXExportSettingsMedia, FBXExportSettings, FBXExportData,
|
FBXExportSettingsMedia, FBXExportSettings, FBXExportData,
|
||||||
)
|
)
|
||||||
@ -3497,6 +3497,14 @@ def save(operator, context,
|
|||||||
ctx_objects = context.view_layer.objects
|
ctx_objects = context.view_layer.objects
|
||||||
if use_visible:
|
if use_visible:
|
||||||
ctx_objects = tuple(obj for obj in ctx_objects if obj.visible_get())
|
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
|
kwargs_mod["context_objects"] = ctx_objects
|
||||||
|
|
||||||
depsgraph = context.evaluated_depsgraph_get()
|
depsgraph = context.evaluated_depsgraph_get()
|
||||||
@ -3528,6 +3536,16 @@ def save(operator, context,
|
|||||||
else:
|
else:
|
||||||
data_seq = tuple((scene, scene.name, 'objects') for scene in bpy.data.scenes if scene.objects)
|
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
|
# call this function within a loop with BATCH_ENABLE == False
|
||||||
|
|
||||||
new_fbxpath = fbxpath # own dir option modifies, we need to keep an original
|
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
|
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. #####
|
# ##### UIDs code. #####
|
||||||
|
|
||||||
# ID class (mere int).
|
# ID class (mere int).
|
||||||
|
Loading…
Reference in New Issue
Block a user