I've spent a lot of time with creating Shape Keys for my model and now I see I have forgot to Apply the Subdivision Surface modifier.
How can I Apply modifier and keep all my Shape Keys?
Thank you for help.
I've spent a lot of time with creating Shape Keys for my model and now I see I have forgot to Apply the Subdivision Surface modifier.
How can I Apply modifier and keep all my Shape Keys?
Thank you for help.
This answer comes from a previous one here Cannot Apply Array Modifier. I simply add some more details in the explanation and a way to do it for all shape keys at once.
Here is a simple shape, which has a subsurface modifier and shape keys :
Make one copy of your initial object for each shape key and set for each the shape key to "1" :
Select these copies and pull down the Object menu and select Convert > Mesh, (for 2.79 and earlier press Alt + C Convert > Mesh from Curve).
The modifiers are applied and the shape keys too (these objects now have no shape keys or subsurf modifiers).
Make another copy of your original object, remove the shape keys from it, and apply the Subsurf modifier :
Now select the "shape keyed" objects, and the last copy we just made (in this order), and in the shape key panel use "join as shapes" :
We now have the final object, with the subsurface modifier applied and the shape keys available :

Yup. I'm trying to get to know it better. I have quite many problems already with UE4 by understanding what can be done or what can't.. For example, today I spent the whole day trying to understand whether I should use one multi-texture for my dragon using UDIM technology or use 3 separated objects instead.
– TheLast_BlenderBender Jun 29 '16 at 00:41Here 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.
EDIT by vklidu: I thought @Ned script is outdated, but the script works even in 3.3.
If there is set just a one Shape Key it works fine ... if there is more Shape Keys, than "Key 1" has to be selected (under Data Properties > Shape Keys) to prevent error.
ERROR (bke.mesh_convert): C:\Users\blender\git\blender-v330\blender.git\source\blender\blenkernel\intern\mesh_convert.cc:1574 BKE_mesh_nomain_to_mesh: YEEK! this should be recoded! Shape key loss!: ID 'MECube.003'.
– BrainSlugs83
Oct 17 '22 at 16:20
I scripted @Lemon 's answer!
the code is kinda long, but I think it is (I hope it to be) self-explanatory. Just copy the code into a new file on blender's text editor, call the text file apply_with_shape_keys.py, and then, select the object with the modifiers and shape keys, (make a backup, just in case). Now from the console execute it like this:
>>> import apply_with_shape_keys
>>> apply_with_shape_keys.super_apply_modifiers()
the script will apply all the modifiers conserving the shape keys! and will create a new object called "backup" without the modifiers applied (just in case you wanna add new shape keys in the future).
If the object has an armature, the script will explicitly not apply that modifier. Also you have to make sure that the skeleton is in the original pose, otherwise strange things will happen.
here is the code!
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 = True
bpy.context.scene.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
select(object)
shape_key_count = len(get_active_block().items())
select_last_shape_key()
for i in range(0, shape_key_count):
bpy.ops.object.shape_key_remove(all=False)
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(apply_as='DATA', 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), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "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 = '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), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "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 = 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)
I just updated the @Bengalaa 's script with @Lemon 's answer to work in blender 2.90
# File: apply_with_shape_keys.py
# usage:
# - select an object to apply modifiers on
# >>> import apply_with_shape_keys
# >>> apply_with_shape_keys.super_apply_modifiers()
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 = True
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
select(object)
shape_key_count = len(get_active_block().items())
select_last_shape_key()
for i in range(0, shape_key_count):
bpy.ops.object.shape_key_remove(all=False)
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(apply_as='DATA', 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 = '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 = 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)
object.select = True which had to be object.select_set(True) and the bpy.context.object.active_shape_key.id_data.name caused errors for some reason running in the Text editor. The armature modifier also got deleted but easy enough to put back (and an additional shape_key Basis was added) Finally I used object.shape_key_clear() to delete all the shape_keys rather than iterating.
– paddyg
Dec 30 '20 at 22:47
Here is fixed version.
Tested with Blender 3.3.1
Edit: copy pasted code from my gist
# https://blender.stackexchange.com/questions/56795/script-outdated-shape-keys-and-applying-subdivision-surface-modifier
import bpy
def reset_shape_keys ():
for name, shape_key in get_active_block().items():
shape_key.value = 0
def get_active_block ():
if bpy.context.object.active_shape_key is None or bpy.context.object.active_shape_key.id_data is None:
bpy.context.object.active_shape_key_index = 0
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)
try:
for key, shape_key in get_active_block().items():
select(backup)
if(key == "Basis"):
continue
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
})
print("key: " + key);
meshed_shape_key = bpy.context.object
meshed_shape_key.name = backup.name + '_' + key
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)
backup.hide_set(True)
except Exception as err:
original_name = original.name
original.name = original.name + "_broken"
backup.name = original_name
select(original)
super_apply_modifiers()
gist version might have other fixes too:
https://gist.github.com/terokorp/c2e0bdadf4cffcc856098b93b84d38a9/
UE4 seems to support this indeed. Shape keys there are called morph targets. However, I haven' t tried that earlier..
With very simple mesh, like cube, it's relatively easy to push or drag the geometry when subsurf is applied, but I have an eyelid which can' t be dragged as easily, even with proportional editing it's quite hard to get the shape I look for because there's too much geometry to handle...
– TheLast_BlenderBender Jun 28 '16 at 13:30