EDIT 2: This now an add-on, available on GitHub: https://github.com/Nanoglyph/Shapekey-Baker
Ever want to edit a mesh after you’ve made shape keys without messing the other shape keys up? Or apply your current shape key mix as your new basis while propogating that change down through all your shape keys?
Then welcome to Shape Key Baker!
(There’s also an option to alphabetize the shape keys).
------------------------------------
Here’s a handy utility to update the Basis and all shape keys so that the current mix is the new basis, and all of the shape keys are updated to be relative to that. Basically scripts the process of doing “New shape from mix” deleting the old, renaming the new over and over again.
EDIT: Bux fixes and improved error handling. There’s also an addon version now which I’ll add in my reply below. This post just has a script you can paste and run from the text editor. Now you can have the first shape key named something different than Basis. Furthermore I’ve corrected it so that old shapekeys aren’t deleted until all of the new ones are created. This fixes a bug with relative relationships that are out of order, though it is a bit slower.
Notes:
- Preserves min/max range values for each shape key, so you don’t have to worry about that.
- Preserves original Basis as a shape key named OriginalBasis that is relative to the new Basis. You can delete it if you want, but the code doesn’t assume.
- Disables Subdivision modifier (if enabled for viewport) for performance, then re-enables (if it had been enabled). No idea the ramifications if you had more than one subdivision modifier.
- Toggles edit mode because otherwise the new Basis won’t register properly and if you delete all shapekeys it’ll revert back to the original shape.
- New keys will be in the same order as the old (aside from OriginalBasis being the second key in the list), and will have the same names.
Caveats:
- Will not preserve vertex groups on shape keys, they will be applied as part of the new shape key instead. It will not carry over any “relative to” relationships either. This is the same behavior as “New shape from mix”
- Still not tested on multires. Advise disabling.
- May be slow on higher poly meshes with 100+ shapekeys. This is why I’ve set it to disable and re-enable the subdiv modifier, but that won’t help if the mesh is just naturally high poly.
import bpy
# Get selected object and shapekeys
ob = bpy.context.object
skeys = ob.data.shape_keys.key_blocks
# Utility variables
oldBasis = 'OriginalBasis' # New name for orinal Basis
subD = False
suffix = '__axpxp'
count = 0
"""PREP WORK"""
# Rename 'OriginalBasis' if it already exists
if bool(skeys.get(oldBasis)):
skeys.get(oldBasis).name = oldBasis + '.Older'
# Disable subdivision for performance, if it exists
try:
if ob.modifiers['Subdivision'].show_viewport == True:
subD = True
ob.modifiers['Subdivision'].show_viewport = False
except:
print('')
# Rename Basis, whether it is at the top of the stack or not
ob.active_shape_key_index = 0
ob.active_shape_key.name = oldBasis
try:
skeys.get('Basis').name = 'Basis.Older'
except:
print('')
"""BAKE NEW BASIS FROM MIX"""
# Create new shape from mix and zero out all shape keys
ob.shape_key_add(name = str('Basis'), from_mix=True)
bpy.ops.object.shape_key_clear()
# Enable and set active shape key to index of Basis
# Then move to index 0
skeys.get('Basis').value = 1
ob.active_shape_key_index = skeys.find('Basis')
bpy.ops.object.shape_key_move(type='TOP')
bpy.ops.object.shape_key_move(type='UP')
# Zero out all shape keys
bpy.ops.object.shape_key_clear()
""" BAKE NEW SHAPE KEYS """
for key in skeys:
if key.name != 'Basis' and key.name != oldBasis:
# Prevent infinite loop by screening out "__apxpx" shape keys
if suffix not in key.name:
count+=1
print("Key " + str(count) + ": " + key.name)
# Preserve current value max value
# And set max to 1 to ensure new key replicates current behavior
kmax = key.slider_max
key.slider_max = 1
# Enable shape key and create new shape key from mix
key.value = 1
ob.shape_key_add(name=key.name + suffix, from_mix=True)
# Copy min/max rang values
skeys.get(key.name + suffix).slider_max = kmax
skeys.get(key.name + suffix).slider_min = key.slider_min
# Zero out all shape keys
bpy.ops.object.shape_key_clear()
""" CLEAN UP """
# Delete original keys and remove temp suffix from new keys
for key in skeys:
if key.name != 'Basis' and key.name != oldBasis:
if suffix not in key.name:
ob.active_shape_key_index = skeys.find(key.name)
bpy.ops.object.shape_key_remove()
else:
key.name = key.name.replace(suffix,'')
# Set Old Basis relative to new Basis
skeys.get(oldBasis).relative_key = skeys.get('Basis')
# This is hacky, but for some reason if we don't toggle edit mode
# the mesh reverts to the original Basis if we don't.
bpy.ops.object.editmode_toggle()
bpy.ops.object.editmode_toggle()
# Re-enable subdvivision if it had been enabled
if subD == True:
ob.modifiers['Subdivision'].show_viewport = True