This is a tool I wrote to help make realtime edits on both blendshapes from one blendshape. There may be some bugs but hope it is helpful.
Helpful Maya Commands: CreateWrap.
Basically the tool scales one mesh in minus x then uses that to wrap to right blendshape hence driving right Blendshape from Left.
Here's a short video I made on its use.
realtimeBlendshape.m4v
cheers,
Nate
##@file na_editBlend.py #make realtime edits of right blendshape by editing left blendshape. #@note if left blendshape is other x axis change mirrorAx to 'Y' or 'Z' it should still work ## ## #@author Nathaniel Anozie #ogbonnawork at gmail.com #Modify at your own risk #how to use this """ import na_editBlend import maya.cmds as cmds reload(na_editBlend) from na_editBlend import Face #finalMeshArg = 'pCube1' defaultMeshArg = 'pCube2' rightBlendArg = 'pCube3' leftBlendArg = 'pCube4' sel = [leftBlendArg,rightBlendArg,defaultMeshArg] cmds.select(sel,replace=True) smoothFace = Face() smoothFace.startSculpting() smoothFace.stopSculpting() """ # #last updated: 08/10/2013 -- working on real rig scene testing #last updated: 08/10/2013 -- added cleaning #last updated: 08/09/2013 -- working on cleaning #last updated: 08/09/2013 -- initial release #last updated: 08/08/2013 -- working on initial release #last updated: 08/07/2013 -- working on initial release #last updated: 08/02/2013 -- working on initial release import maya.cmds as cmds import maya.mel as mel import sys from pprint import pprint class Face(object): def __init__(self, mirrorAx = 'X'): sel = cmds.ls(selection=True) if len(sel) == 3: leftBlendArg = sel[0] rightBlendArg = sel[1] defaultMeshArg = sel[2] else: cmds.error('Select Left Mesh, Right Mesh, then Default Mesh!!!\n') sys.exit() self.mirrorAx = mirrorAx #could be Y or Z #self.finalMeshArg = finalMeshArg #cmds.duplicate(finalMeshArg) self.defaultMeshArg = defaultMeshArg self.rightBlendArg = rightBlendArg self.rightBlendNodeName = 'right_blend' #should error out if a blend node is name this already self.leftBlendArg = leftBlendArg #duplicate defaultMesh twice making intermediate1, and scaleMinusMesh intermediate1Ar = cmds.duplicate(defaultMeshArg) intermediate1 = intermediate1Ar[0] scaleMinusMeshAr = cmds.duplicate(defaultMeshArg) scaleMinusMesh = scaleMinusMeshAr[0] dupRightBlendAr = cmds.duplicate(rightBlendArg) dupRightBlend = dupRightBlendAr[0] ## self.intermediate1 = intermediate1 self.scaleMinusMesh = scaleMinusMesh self.dupRightBlend = dupRightBlend #brainstorming how to clean up all the mess this makes and still have clean meshes that have edits print('it might make sense to copy meshes from scene. and have something to replace a blendshape with a different mesh. replacement should preserve blendshape use of animator controls ex: expressions, connections ... \n') def startSculpting(self): print('[startSculpting] Begin Presetup for sculpting on blendshape ...\n') ## mirrorAxis = self.mirrorAx #finalMesh = self.finalMeshArg defaultMesh = self.defaultMeshArg rightBlend = self.rightBlendArg rightBlendNodeName = self.rightBlendNodeName leftBlend = self.leftBlendArg intermediate1 = self.intermediate1 scaleMinusMesh = self.scaleMinusMesh dupRightBlend = self.dupRightBlend ## #pprint( [defaultMesh,rightBlend,leftBlend,intermediate1] ) #move to rightBlend, intermediate1 and scaleMinusMesh (world space) cntAr1 = cmds.parentConstraint(rightBlend,intermediate1, mo = 0) cmds.delete( cntAr1[0] ) cntAr2 = cmds.parentConstraint(rightBlend,scaleMinusMesh, mo = 0) cmds.delete( cntAr2[0] ) cntAr3 = cmds.parentConstraint(rightBlend,dupRightBlend, mo = 0) cmds.delete( cntAr3[0] ) #set scaleX -1 scaleMinusMesh cmds.setAttr( (scaleMinusMesh + ".scale"+mirrorAxis), -1.0 ) #make leftBlend -> intermediate1 (via intermediate1_blend) cmds.blendShape(leftBlend,intermediate1,n = 'intermediate1_blend') #make intermediate1 -> scaleMinusMesh (via scaleMinusMesh_blend) cmds.blendShape(intermediate1,scaleMinusMesh,n = 'scaleMinusMesh_blend') #Try moving scale Here ???? #put intermediate1 blendWeight at 1 (via intermediate1_blend) cmds.setAttr( 'intermediate1_blend.w[0]', 1.0 ) #put scaleMinusMesh blendWeight at 1 (via scaleMinusMesh_blend) cmds.setAttr( 'scaleMinusMesh_blend.w[0]', 1.0 ) ##This should make right blendshape be edited in realtime #### ##can connect scale - mesh directly to right blend need a mesh in between #make scaleMinusMesh -> dupRightBlend via wrap, because vertex order different #make sure duplicate right blendshape mesh looks like the right blendshape print '[startSculpting] Making scaled mesh %s -- drive duplicate of right blendshape %s \n' %(scaleMinusMesh,dupRightBlend) cmds.select(dupRightBlend,replace = True) cmds.select(scaleMinusMesh,add = True) mel.eval("CreateWrap") #cmds.deformer( scaleMinusMesh, type = 'wrap' ) #LOOK UP THE CORRECT WRAP DEFORMER COMMAND #### #make dupRightBlend -> rightBlend (via rightBlend_blend), put rightBlend blendWeight at 1 (via rightBlend_blend) cmds.blendShape(dupRightBlend,rightBlend,n = rightBlendNodeName) cmds.setAttr( 'right_blend.w[0]', 1.0 ) #so can start editing left blend hide some things, should only see finalMesh and leftBlend, finalMesh should get updated on both sides in realtime hideAr = [rightBlend,defaultMesh,intermediate1,scaleMinusMesh,dupRightBlend] [ cmds.setAttr( (x+'.visibility'),0 ) for x in hideAr ] def stopSculpting(self): #delete wraps, blendshapes, extra meshes, okay to delete history on meshes print('[stopSculpting] Begin Finishing ...\n') """ self.intermediate1 = intermediate1 self.scaleMinusMesh = scaleMinusmesh self.dupRightBlend = dupRightBlend self.rightBlendNodeName = 'right_blend' """ #clean up right blendshape not messing with its outputs rightBlendShapeAr = cmds.pickWalk(self.rightBlendArg, direction = 'down') rightBlendShape = rightBlendShapeAr[0] cmds.disconnectAttr( self.rightBlendNodeName+'.'+'outputGeometry[0]', rightBlendShape+'.'+'inMesh' ) #remove extras cmds.delete(self.rightBlendNodeName) cmds.delete(self.intermediate1) cmds.delete(self.scaleMinusMesh) cmds.delete(self.dupRightBlend)