Thanks for your interest. Yeah, that code isn’t intended to be runnable by anyone so much as I posted it to show the conceptual framework of the script, sorry about that.
I’m still pretty new to programming and there’s a lot of different stuff I’m trying to put together which kind of overwhelms me at times, especially regarding animation.
I’m sorry though, I still haven’t finished that script completely. I have it in working order, along with a script that’s a little better at mirroring shape keys(arguably, I’d say), but I still want to improve the details before I post a functional version here.
In the meantime, I do have something else I just completed, though. To liberally sum it up, it applies an object’s first modifier whether or not the object has shape keys, and works with subd, solidify, and some others.
import bpy
obj = bpy.context.scene.objects.active
mesh = bpy.data.objects[obj.name].data.name
keys = bpy.context.scene.objects.active.data.shape_keys.key_blocks
askindex = obj.active_shape_key_index
askey = obj.active_shape_key
data = bpy.data
objs = bpy.data.objects
def getobjindex(object):
count = 0
for ob in objs:
if ob == object:
objindex = count
break
count+=1
return objindex
def create_modified_duplicate():
modified = obj.copy()
modified.name = "modified"
modmesh = modified.data.name
modmeshdup = bpy.data.meshes[modmesh].copy()
modified.data = modmeshdup
modkeys = modified.data.shape_keys.key_blocks
modmods = modified.modifiers
bpy.context.scene.objects.link(modified)
bpy.context.scene.objects.active = modified
bpy.context.scene.update()
modified.select = True
obj.select = False
bpy.context.scene.objects.active = modified
bpy.ops.object.shape_key_remove(all=True)
bpy.ops.object.modifier_apply(apply_as='DATA',modifier = modmods[0].name)
return modified
modified = create_modified_duplicate()
bpy.context.scene.objects.active = obj
obj.select = True
objindex = getobjindex(obj)
count = 0
sos = obj.show_only_shape_key
obj.show_only_shape_key = True
for shape in keys:
if count > 0:
obj.active_shape_key_index = count
bpy.ops.object.object_duplicate_flatten_modifiers()
shapeobj = objs[obj.name + "_clean"]
shapeobj.name = obj.active_shape_key.name
shapeobj.select = True
bpy.context.scene.objects.active = modified
modified.select = True
obj.select = False
bpy.ops.object.join_shapes()
bpy.context.scene.objects.active = obj
bpy.context.scene.objects.unlink(shapeobj)
count +=1
obj.show_only_shape_key = sos
Quick notes:
- You’ll need to enable the Corrective Shape Keys addon to use this.
- DO NOT use this on a complicated object with a lot of shape keys. Depending on the strength of your computer it may be OK, but if you have 100 shapes on a million-vertex object or something like that, it’s probably going to create a heavy memory usage on your computer.
- I’ve tested it with subdivision surface and solidify, at least. It should work with others as well.
- Examples of modifiers it’s not likely to work with are Edge Split(it will work if you’re only using sharp marks, though), decimate, bevel, mirror with the merge option on–basically, anything that will result in varying vertex counts between different shapes for whatever reason. But, there shouldn’t be any harm in trying anyway.
- Make sure you only have one modifier on the object. Sorry about that, I should be able to fix it easily some other time.
If you run this script on an object with a modifier that DOES change vertex count but results in the same vertex count and order no matter what the shape is, then this script should generate a new object(called “modified”) that has modifiers applied and possesses all of the original’s shape keys. Vertex groups and all other data should remain intact, since the new object is just a duplicate of the original with the modifier applied.
It may not work correctly on all scenes or objects, but in general this is the flow of what it does:
- Duplicate the active object
- Duplicate the active object’s mesh and assign the duplicate object the new mesh(so they don’t share shape key collections)
- Clear the duplicate object’s shape keys
- Apply the first modifier on the object
- Iterate over the active object’s shape keys:
4a)use the bpy.ops.object.object_duplicate_flatten_modifiers() operator on each(creates a duplicate object from the shape key with all modifiers applied), give it the name of the active shape key
4b)select the duplicate object, make it the active object, use Join As Shapes
4c)remove the object created in 4a
4d)set the active object to the original object, repeat
One last note. There is a bug I’ve encountered quite often in Blender where the actual shape of the model is registered as something that is not equal to any of the shapes on the list. If you use the “delete all shapes” option, sometimes your model will become splattered(for lack of a better word), which is the “actual state” of the model(I guess). Since this script uses the “delete all shapes” operator, I’m not sure how this script will function when that happens, but you should be able to fix it by making a copy of the basis shape and then making that copy the new basis shape by deleting the current basis shape while the copy is directly under it(sorry if that’s confusing).
If anyone has problems please let me know.
EDIT: Made a slight revision to the code, if it didn’t work before try again
EDIT: And another…
EDIT3:
Ahh, while I’m at it, here’s this:
import math
import bpy
from numpy import array
from mathutils import Vector
import os
os.chdir("C:/Users/Me/Documents/pythonfiles")
import mathutils
p = print
import bmesh
obj = bpy.context.scene.objects.active
mesh = bpy.data.objects[obj.name].data.name
verts = bpy.data.meshes[mesh].vertices
keys = obj.data.shape_keys.key_blocks
askeyv = obj.active_shape_key.value
sosk = obj.show_only_shape_key
for ob2 in bpy.context.selected_objects:
if ob2.type == 'MESH':
if ob2.name == obj.name:
pass
else:
selobj = ob2
selobkeys = ob2.data.shape_keys.key_blocks
count = 0
for key in selobkeys:
if count == 0:
pass
else:
shape = selobkeys[count]
keyname = shape.name
newkey = obj.shape_key_add(name=keyname, from_mix=False)
countverts = 0
for v in verts:
keys[-1].data[countverts].co = shape.data[countverts].co
countverts +=1
count +=1
This just transfers all of the shape keys of the active object to the first selected object. It should run even if the vertex count isn’t the same, but it won’t work correctly if the objects’ vertex order isn’t the same from 0. This is actually a little important, because it means if you, say, add a hat to a character, you could still run the code on it and it would work since the hat would simply be appended to the end of the vertex list and the vertex order is otherwise the same.
This goes for both of the scripts I posted, but sorry if there are some useless bits of code in there. If anyone’s looking at it to study(I’d be honored but I don’t advise it) be aware that the code isn’t polished and some things are just left over from testing and such(or I don’t remember if they were necessary and too lazy atm to review the code and check).
As always if anyone has comments/criticism/suggestions/etc. I’d be more than happy to hear it.