Here is an updated script which is tested and working for me on 2.91.2. The script from the existing answers still wasn't working for me in 2.9, so I took into account the fixes mentioned the comment by @paddyg (also had to remove apply_as='DATA'
from modifier_apply since that is deprecated). It still creates a duplicate Basis.001 shape key (which you can just delete) but otherwise I think it works properly.
To use it, just paste this code into the Blender text editor, select your object, then click the Run Script button (little play button in the text editor header).
(I added the call to super_apply_modifiers() at the end so you could just run it from the text editor. This seemed easier to me than running it from the console, but you can remove the last line if you want to do it the other way.)
import bpy
def reset_shape_keys ():
for name, shape_key in get_active_block().items():
shape_key.value = 0
def get_active_block ():
block_id = bpy.context.object.active_shape_key.id_data.name
return bpy.data.shape_keys[block_id].key_blocks
def select (selection):
bpy.ops.object.select_all(action='DESELECT')
selection.select_set(True)
bpy.context.view_layer.objects.active = selection
def select_last_shape_key ():
shape_key_count = len(get_active_block().items())
bpy.context.object.active_shape_key_index = shape_key_count - 1
def remove_shape_keys (object):
selection = bpy.context.object
object.shape_key_clear()
select(selection)
def apply_modifiers (object):
selection = bpy.context.object
select(object)
for key, modifier in object.modifiers.items():
if key != 'Armature':
bpy.ops.object.modifier_apply(modifier=key)
select(selection)
def super_apply_modifiers ():
original = bpy.context.object
bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={
"linked":False, "mode":'TRANSLATION'},
TRANSFORM_OT_translate={
"value":(0, 0, 0),
"constraint_axis":(False, False, False),
"orient_matrix":[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
"orient_matrix_type":'GLOBAL',
"mirror":False,
"use_proportional_edit":False,
"proportional_edit_falloff":'SMOOTH',
"proportional_size":1,
"snap":False, "snap_target":'CLOSEST',
"snap_point":(0, 0, 0),
"snap_align":False,
"snap_normal":(0, 0, 0),
"gpencil_strokes":False,
"texture_space":False,
"remove_on_cancel":False,
"release_confirm":False,
"use_accurate":False
})
backup = bpy.context.object
backup.name = original.name + '_backup'
remove_shape_keys(original)
apply_modifiers(original)
for key, shape_key in get_active_block().items():
select(backup)
bpy.ops.object.duplicate_move(
OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'},
TRANSFORM_OT_translate={
"value":(0, 0, 0),
"constraint_axis":(False, False, False),
"orient_matrix":[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
"orient_matrix_type":'GLOBAL',
"mirror":False,
"use_proportional_edit":False,
"proportional_edit_falloff":'SMOOTH',
"proportional_size":1,
"snap":False,
"snap_target":'CLOSEST',
"snap_point":(0, 0, 0),
"snap_align":False,
"snap_normal":(0, 0, 0),
"gpencil_strokes":False,
"texture_space":False,
"remove_on_cancel":False,
"release_confirm":False,
"use_accurate":False
})
meshed_shape_key = bpy.context.object
select(meshed_shape_key)
reset_shape_keys()
get_active_block()[key].value = 1
bpy.ops.object.convert(target='MESH')
select(original)
meshed_shape_key.select_set(True)
bpy.ops.object.join_shapes()
select_last_shape_key()
bpy.context.object.active_shape_key.name = key
select(meshed_shape_key)
bpy.ops.object.delete(use_global=False)
super_apply_modifiers()
Thank you everyone for the other answers, I had done a bunch of modeling using multiple shape keys with a subsurf modifier on the whole time, without realizing this isn't the "proper" workflow, and then got slapped with the "modifier cannot be applied to a mesh with shape keys" error.
EDIT: When I first posted this, I'd only tested in 2.90.1. It broke again in 2.91.2, but after updating remove_shape_keys()
to use object.shape_key_clear()
instead of iterating, as suggested by @paddyg, it works again.