So in the process of creating corrective blendshapes for my character using the awesome BSpiritCorrectiveShape script, I ran into the issue of not having symmetrical blendshapes. I researched it a bit and this is the solution I was able to come up with.
I think of it as a sort of “inflated mold”. It uses the Wrap deformer, something I was unfamiliar with until researching this. The Wrap deformer seems to work like a Lattice deformer just with a complex object cage instead of a simple cube.
The benefit of that complex cage shape is that you can essentially have one mesh copy the other as if it is being inflated from the inside. This is different from a blendshape because it isn’t vertex number dependent.
And that’s an important note. Why not just flip your blendshape on the x axis and use that as the mirrored blendshape? The problem is, it doesn’t work that way. Imagine I have points [a,b,c] in space. If I flip them on the “b” axis, we get [c,b,a]. To get our blendshape to work, we need to have the mirrored shape that [c,b,a] gives us but with the vertex order of [a,b,c]. So here’s the solution.
- Duplicate your default mesh twice. These are targetGeo and inverseGeo.
- Apply the blendshape you want mirrored to targetGeo and scale targetGeo -1 on the x axis. Don’t turn on the blendshape though.
- Apply a wrap deformer to your inverseGeo shape. The selection order is inverseGeo, targetGeo, or as I like to think of it, inside/out (out being our cage)
- Turn on the blendshape on targetGeo. this will now basically “inflate” the inverseGeo mesh, giving us the nice [c,b,a] shape with the vertex order of [a,b,c]
- This is a huge computation though and I found it lagged pretty badly on meshes over 3-4k verts… That’s the biggest downfall of this script; it’s simple but not fast on high resolution meshes.
Here’s the tool I wrote to do this for you. You select your base mesh and then your blendshape you want to be mirrored and run it. It returns a new mirrored mesh.
import maya.cmds as cmds # takes your base mesh (1) and the shape that you want to mirror (2) and creates the inverse blendshape # assumes we want to mirror in -x def mirrorBlendshape(): sel = cmds.ls(sl=1, o=1) base = sel shape = sel vtxCount = cmds.polyEvaluate(base, vertex=1) if vtxCount > 4500: cmds.confirmDialog(title="Warning", message="High vertex count (%i). This might take a while..." % vtxCount) else: print "%i verts, this shouldn't take long." % vtxCount # target mesh is the one that will recieve the blendshape and be mirrored target = cmds.duplicate(base, returnRootsOnly=1) # the invShape mesh is the one that will be the final inverted blenshape invShape = cmds.duplicate(base, returnRootsOnly=1) # create our blendshape, mirror the mesh, create the wrap deformer, and apply the blendshape bs = cmds.blendShape(shape, target) cmds.xform(target, s=(-1, 1, 1)) cmds.select(invShape, target, add=1) cmds.CreateWrap() # "inflate" our mesh to the correct mirrored position using our blendshape. BIG calculation cmds.setAttr(bs + "." + shape, 1) hist = cmds.listHistory(invShape) index = hist.index(target + "BaseShape") # delete the history on our final shape so that we can delete the shape we used to make it cmds.delete(invShape, constructionHistory=1) cmds.select(target + "BaseShape") x = cmds.pickWalk(d="up") # delete our base shape node cmds.delete(x) # delete our target shape so we're just left with the shape we want cmds.delete(target) return invShape mirrorBlendshape()