def test_avar_connection_persistence(self): import omtk from omtk.modules import rigHead from omtk.modules import rigFaceJaw from omtk.modules import rigFaceLips from omtk.libs import libRigging # Create a base rig rig = omtk.create() rig.add_module(rigHead.Head([pymel.PyNode('jnt_head')])) module_jaw = rig.add_module(rigFaceJaw.FaceJaw([pymel.PyNode('jnt_jaw')])) module_lips = rig.add_module(rigFaceLips.FaceLips(pymel.ls('jnt_lip*', type='joint'))) rig.build(strict=True) # Connect some avars avar_src = next(iter(module_jaw.avars), None).attr_ud for avar in module_lips.avars: avar_dst = avar.attr_ud libRigging.connectAttr_withLinearDrivenKeys(avar_src, avar_dst) # Re-build the rig rig.unbuild(strict=True) rig.build(strict=True) # Ensure the avars are still connected. avar_src = next(iter(module_jaw.avars), None).attr_ud avar_src.set(1.0) for avar in module_lips.avars: avar_dst = avar.attr_ud self.assertAlmostEqual(avar_dst.get(), 1.0)
def _connect_avar_macro_r(self, avar, child_avars): super(FaceLips, self)._connect_avar_macro_r(avar, child_avars) # Connect the corner other avars avar_r_corner = self.get_avar_r_corner() if avar_r_corner and avar_r_corner in child_avars: libRigging.connectAttr_withLinearDrivenKeys(avar.attr_ud, avar_r_corner.attr_ud) libRigging.connectAttr_withLinearDrivenKeys(avar.attr_lr, avar_r_corner.attr_lr)
def _build_avar_macro_r(self):# Create right avar if necessary ref = self.get_jnt_r_mid() if self.create_macro_horizontal and ref: # Create l ctrl if not self.avar_r: self.avar_r = self.create_avar_macro_right(self._CLS_CTRL_RGT, ref) self._build_avar_macro_horizontal(self.avar_r, self.get_avar_mid(), self.get_avars_r(), self._CLS_CTRL_RGT, connect_lr=True, connect_ud=False, connect_fb=False) avar_r_corner = self.get_avar_r_corner() if avar_r_corner: libRigging.connectAttr_withLinearDrivenKeys(self.avar_r.attr_ud, avar_r_corner.attr_ud) libRigging.connectAttr_withLinearDrivenKeys(self.avar_r.attr_fb, avar_r_corner.attr_fb)
def _build_avar_macro_l(self): # Create left avar if necessary ref = self.get_jnt_l_mid() if self.create_macro_horizontal and ref: if not self.avar_l: self.avar_l = self.create_avar_macro_left(self._CLS_CTRL_LFT, ref) self._build_avar_macro_horizontal(self.avar_l, self.get_avar_mid(), self.get_avars_l(), self._CLS_CTRL_LFT, connect_lr=True, connect_ud=False, connect_fb=False) # Connect the corner other avars avar_l_corner = self.get_avar_l_corner() if avar_l_corner: libRigging.connectAttr_withLinearDrivenKeys(self.avar_l.attr_ud, avar_l_corner.attr_ud) libRigging.connectAttr_withLinearDrivenKeys(self.avar_l.attr_fb, avar_l_corner.attr_fb)
def connect_ctrl(self, ctrl, **kwargs): attr_pt_inn = ctrl.translateY attr_yw_inn = ctrl.translateX # UD Low attr_pt_low = libRigging.create_utility_node('multiplyDivide', input1X=attr_pt_inn, input2X=-1).outputX ''' attr_pt_inn = libRigging.create_utility_node('condition', operation=4, # Less than firstTerm=attr_pt_inn, colorIfTrueR=attr_pt_low, colorIfFalseR=0.0 ).outColorR ''' libRigging.connectAttr_withLinearDrivenKeys( attr_pt_low, self.attr_pt, kv=[0.0, 0.0, 10.0] ) libRigging.connectAttr_withLinearDrivenKeys( attr_yw_inn, self.attr_yw, kv=[-5.0, 0.0, 5.0] )
def _build_avar_macro_r(self): # Create right avar if necessary ref = self.get_jnt_r_mid() if self.create_macro_horizontal and ref: # Create l ctrl if not self.avar_r: self.avar_r = self.create_avar_macro_right( self._CLS_CTRL_RGT, ref) self._build_avar_macro_horizontal(self.avar_r, self.get_avar_mid(), self.get_avars_r(), self._CLS_CTRL_RGT, connect_lr=True, connect_ud=False, connect_fb=False) avar_r_corner = self.get_avar_r_corner() if avar_r_corner: libRigging.connectAttr_withLinearDrivenKeys( self.avar_r.attr_ud, avar_r_corner.attr_ud) libRigging.connectAttr_withLinearDrivenKeys( self.avar_r.attr_fb, avar_r_corner.attr_fb)
def _build_avar_macro_all( self, connect_ud=True, connect_lr=True, connect_fb=True, constraint=False, follow_mesh=True, **kwargs ): # Create all avar if necessary # Note that the use can provide an influence. # If no influence was found, we'll create an 'abstract' avar that doesn't move anything. if self.create_macro_all: # We'll always want to macro avar to be positionned at the center of the plane. pos = libRigging.get_point_on_surface_from_uv(self.surface, 0.5, 0.5) jnt_tm = pymel.datatypes.Matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, pos.x, pos.y, pos.z, 1) # If we don't have any influence, we want to follow the surface instead of the character mesh.. follow_mesh = True if self.avar_all.jnt else False self._build_avar_macro( self._CLS_CTRL_ALL, self.avar_all, jnt_tm=jnt_tm, ctrl_tm=jnt_tm, obj_mesh=self.surface, follow_mesh=follow_mesh, constraint=constraint, ) for avar_child in self.avars: if connect_ud: libRigging.connectAttr_withLinearDrivenKeys(self.avar_all.attr_ud, avar_child.attr_ud) if connect_lr: libRigging.connectAttr_withLinearDrivenKeys(self.avar_all.attr_lr, avar_child.attr_lr) if connect_fb: libRigging.connectAttr_withLinearDrivenKeys(self.avar_all.attr_fb, avar_child.attr_fb)
def _build_avar_macro_horizontal(self, avar_parent, avar_middle, avar_children, cls_ctrl, connect_ud=False, connect_lr=True, connect_fb=False): self._build_avar_macro(cls_ctrl, avar_parent) pos_s = avar_middle.jnt.getTranslation(space='world') pos_e = avar_parent.jnt.getTranslation(space='world') for avar_child in avar_children: # We don't want to connect the middle Avar. if avar_child == avar_middle: continue pos = avar_child.jnt.getTranslation(space='world') # Compute the ratio between the middle and the corner. # ex: In the lips, we want the lips to stretch when the corner are taken appart. ratio = (pos.x - pos_s.x) / (pos_e.x - pos_s.x) ratio = max(0, ratio) ratio = min(ratio, 1) if connect_ud: libRigging.connectAttr_withLinearDrivenKeys(avar_parent.attr_ud, avar_child.attr_ud) if connect_lr: libRigging.connectAttr_withLinearDrivenKeys(avar_parent.attr_lr, avar_child.attr_lr, kv=(-ratio,0.0,ratio)) if connect_fb: libRigging.connectAttr_withLinearDrivenKeys(avar_parent.attr_fb, avar_child.attr_fb)
def _build_avar_macro_l(self): # Create left avar if necessary ref = self.get_jnt_l_mid() if self.create_macro_horizontal and ref: if not self.avar_l: self.avar_l = self.create_avar_macro_left( self._CLS_CTRL_LFT, ref) self._build_avar_macro_horizontal(self.avar_l, self.get_avar_mid(), self.get_avars_l(), self._CLS_CTRL_LFT, connect_lr=True, connect_ud=False, connect_fb=False) # Connect the corner other avars avar_l_corner = self.get_avar_l_corner() if avar_l_corner: libRigging.connectAttr_withLinearDrivenKeys( self.avar_l.attr_ud, avar_l_corner.attr_ud) libRigging.connectAttr_withLinearDrivenKeys( self.avar_l.attr_fb, avar_l_corner.attr_fb)
def connect_ctrl(self, ctrl, **kwargs): attr_pt_inn = ctrl.translateY attr_yw_inn = ctrl.translateX # UD Low attr_pt_low = libRigging.create_utility_node('multiplyDivide', input1X=attr_pt_inn, input2X=-1).outputX ''' attr_pt_inn = libRigging.create_utility_node('condition', operation=4, # Less than firstTerm=attr_pt_inn, colorIfTrueR=attr_pt_low, colorIfFalseR=0.0 ).outColorR ''' libRigging.connectAttr_withLinearDrivenKeys(attr_pt_low, self.attr_pt, kv=[0.0, 0.0, 15.0]) libRigging.connectAttr_withLinearDrivenKeys(attr_yw_inn, self.attr_yw, kv=[-5.0, 0.0, 5.0])
def _connect_avar_macro_horizontal(self, avar_parent, avar_children, connect_ud=True, connect_lr=True, connect_fb=True): """ Connect micro avars to horizontal macro avar. (avar_l and avar_r) This configure the avar_lr connection differently depending on the position of each micro avars. The result is that the micro avars react like an 'accordion' when their macro avar_lr change. :param avar_parent: The macro avar, source of the connections. :param avar_children: The micro avars, destination of the connections. :param connect_ud: True if we want to connect the avar_ud. :param connect_lr: True if we want to connect the avar_lr. :param connect_fb: True if we want to connect the avar_fb. """ # super(FaceLips, self)._connect_avar_macro_horizontal(avar_parent, avar_children, connect_ud=False, connect_lr=False, connect_fb=False) if connect_lr: avar_middle = self.get_avar_mid() pos_s = avar_middle.jnt.getTranslation(space='world') pos_e = avar_parent.jnt.getTranslation(space='world') for avar_child in avar_children: # We don't want to connect the middle Avar. if avar_child == avar_middle: continue pos = avar_child.jnt.getTranslation(space='world') # Compute the ratio between the middle and the corner. # ex: In the lips, we want the lips to stretch when the corner are taken appart. try: ratio = (pos.x - pos_s.x) / (pos_e.x - pos_s.x) except ZeroDivisionError: continue ratio = max(0, ratio) ratio = min(ratio, 1) libRigging.connectAttr_withLinearDrivenKeys(avar_parent.attr_lr, avar_child.attr_lr, kv=(-ratio, 0.0, ratio))
def _build_avar_macro_vertical( self, avar_parent, avar_middle, avar_children, cls_ctrl, connect_ud=True, connect_lr=True, connect_fb=True, **kwargs ): self._build_avar_macro(cls_ctrl, avar_parent, **kwargs) for child_avar in avar_children: if connect_ud: libRigging.connectAttr_withLinearDrivenKeys(avar_parent.attr_ud, child_avar.attr_ud) if connect_lr: libRigging.connectAttr_withLinearDrivenKeys(avar_parent.attr_lr, child_avar.attr_lr) if connect_fb: libRigging.connectAttr_withLinearDrivenKeys(avar_parent.attr_fb, child_avar.attr_fb)
def _build_avar_macro_vertical(self, avar_parent, avar_middle, avar_children, cls_ctrl, connect_ud=True, connect_lr=True, connect_fb=True, **kwargs): self._build_avar_macro(cls_ctrl, avar_parent, **kwargs) for child_avar in avar_children: if connect_ud: libRigging.connectAttr_withLinearDrivenKeys( avar_parent.attr_ud, child_avar.attr_ud) if connect_lr: libRigging.connectAttr_withLinearDrivenKeys( avar_parent.attr_lr, child_avar.attr_lr) if connect_fb: libRigging.connectAttr_withLinearDrivenKeys( avar_parent.attr_fb, child_avar.attr_fb)
def _build_avar_macro_all(self, connect_ud=True, connect_lr=True, connect_fb=True, constraint=False, follow_mesh=True, **kwargs): # Create all avar if necessary # Note that the use can provide an influence. # If no influence was found, we'll create an 'abstract' avar that doesn't move anything. if self.create_macro_all: # We'll always want to macro avar to be positionned at the center of the plane. pos = libRigging.get_point_on_surface_from_uv( self.surface, 0.5, 0.5) jnt_tm = pymel.datatypes.Matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, pos.x, pos.y, pos.z, 1) # If we don't have any influence, we want to follow the surface instead of the character mesh.. follow_mesh = True if self.avar_all.jnt else False self._build_avar_macro(self._CLS_CTRL_ALL, self.avar_all, jnt_tm=jnt_tm, ctrl_tm=jnt_tm, obj_mesh=self.surface, follow_mesh=follow_mesh, constraint=constraint) for avar_child in self.avars: if connect_ud: libRigging.connectAttr_withLinearDrivenKeys( self.avar_all.attr_ud, avar_child.attr_ud) if connect_lr: libRigging.connectAttr_withLinearDrivenKeys( self.avar_all.attr_lr, avar_child.attr_lr) if connect_fb: libRigging.connectAttr_withLinearDrivenKeys( self.avar_all.attr_fb, avar_child.attr_fb)
def _build_avar_macro_horizontal(self, avar_parent, avar_middle, avar_children, cls_ctrl, connect_ud=False, connect_lr=True, connect_fb=False): self._build_avar_macro(cls_ctrl, avar_parent) pos_s = avar_middle.jnt.getTranslation(space='world') pos_e = avar_parent.jnt.getTranslation(space='world') for avar_child in avar_children: # We don't want to connect the middle Avar. if avar_child == avar_middle: continue pos = avar_child.jnt.getTranslation(space='world') # Compute the ratio between the middle and the corner. # ex: In the lips, we want the lips to stretch when the corner are taken appart. ratio = (pos.x - pos_s.x) / (pos_e.x - pos_s.x) ratio = max(0, ratio) ratio = min(ratio, 1) if connect_ud: libRigging.connectAttr_withLinearDrivenKeys( avar_parent.attr_ud, avar_child.attr_ud) if connect_lr: libRigging.connectAttr_withLinearDrivenKeys( avar_parent.attr_lr, avar_child.attr_lr, kv=(-ratio, 0.0, ratio)) if connect_fb: libRigging.connectAttr_withLinearDrivenKeys( avar_parent.attr_fb, avar_child.attr_fb)
def connect(self, avar, avar_grp, ud=True, fb=True, lr=True, yw=True, pt=True, rl=True, sx=True, sy=True, sz=True): libRigging.connectAttr_withLinearDrivenKeys( self.ctrl.translateX, avar.attr_lr ) libRigging.connectAttr_withLinearDrivenKeys( self.ctrl.translateY, avar.attr_ud, ) libRigging.connectAttr_withLinearDrivenKeys( self.ctrl.translateZ, avar.attr_fb ) libRigging.connectAttr_withLinearDrivenKeys( self.ctrl.rotateX, avar.attr_pt ) libRigging.connectAttr_withLinearDrivenKeys( self.ctrl.rotateY, avar.attr_yw ) libRigging.connectAttr_withLinearDrivenKeys( self.ctrl.rotateZ, avar.attr_rl )
def _build_avars(self, **kwargs): super(FaceNose, self)._build_avars(**kwargs) # Create a ctrl that will control the whole nose ref = self.inf_nose_low if not self.avar_main: #self.avar_main = self.create_avar_macro(rig, self._CLS_CTRL, ref, name=self.name) self.avar_main = self._create_avar(ref, cls_ctrl=self._CLS_CTRL, name=self.name) self._build_avar_macro(self._CLS_CTRL, self.avar_main) ''' ctrl_upp_name = nomenclature_anm.resolve() if not isinstance(self.ctrl_main, self._CLS_CTRL): self.ctrl_main = self._CLS_CTRL() self.ctrl_main.build(rig, self.inf_nose_low, name=ctrl_upp_name) #self.create_ctrl_macro(rig, self.ctrl_main, self.inf_nose_low) ''' for avar in self.avars: libRigging.connectAttr_withLinearDrivenKeys(self.avar_main.attr_ud, avar.attr_ud) libRigging.connectAttr_withLinearDrivenKeys(self.avar_main.attr_lr, avar.attr_lr) libRigging.connectAttr_withLinearDrivenKeys(self.avar_main.attr_fb, avar.attr_fb) if self.avar_nose_upp: libRigging.connectAttr_withLinearDrivenKeys(self.avar_main.attr_pt, self.avar_nose_upp.attr_pt) libRigging.connectAttr_withLinearDrivenKeys(self.avar_main.attr_rl, self.avar_nose_upp.attr_rl) if self.avar_nose_low: libRigging.connectAttr_withLinearDrivenKeys(self.avar_main.attr_yw, self.avar_nose_low.attr_yw) if self.parent: pymel.parentConstraint(self.parent, self.avar_nose_upp._grp_offset, maintainOffset=True) nose_upp_out = self.avar_nose_upp._stack.node for avar in self.avars: if avar is self.avar_nose_upp: continue avar_inn = avar._grp_offset pymel.parentConstraint(nose_upp_out, avar_inn, maintainOffset=True)
def build(self, calibrate=True, use_football_interpolation=False, **kwargs): """ :param calibrate: :param use_football_interpolation: If True, the resolved influence of the jaw on each lips avar will give a 'football' shape. It is False by default since we take in consideration that the weightmaps follow the 'Art of Moving Points' theory and already result in a football shape if they are uniformly translated. :param kwargs: :return: """ super(FaceLips, self).build(calibrate=False, **kwargs) if not self.preDeform: # Resolve the head influence jnt_head = self.get_head_jnt() if not jnt_head: self.error("Failed parenting avars, no head influence found!") return jnt_jaw = self.get_jaw_jnt() if not jnt_jaw: self.error("Failed parenting avars, no jaw influence found!") return min_x, max_x = self._get_mouth_width() mouth_width = max_x - min_x def connect_avar(avar, ratio): avar._attr_inn_jaw_ratio_default.set(ratio) for avar in self.get_avars_corners(macro=False): connect_avar(avar, 0.5) for avar in self.get_avars_upp(macro=False): if use_football_interpolation: avar_pos_x = avar.grp_offset.tx.get() ratio = abs(avar_pos_x - min_x / mouth_width) ratio = max(min(ratio, 1.0), 0.0) # keep ratio in range ratio = libRigging.interp_football(ratio) # apply football shape else: ratio = 0.0 connect_avar(avar, ratio) for avar in self.get_avars_low(macro=False): if use_football_interpolation: avar_pos_x = avar.grp_offset.tx.get() ratio = abs(avar_pos_x - min_x / mouth_width) ratio = max(min(ratio, 1.0), 0.0) # keep ratio in range ratio = 1.0 - libRigging.interp_football(ratio) # apply football shape else: ratio = 1.0 connect_avar(avar, ratio) # Hardcode the jawRatio for the macro ctrls self.avar_upp._attr_inn_jaw_ratio_default.set(0.0) self.avar_l._attr_inn_jaw_ratio_default.set(0.5) self.avar_r._attr_inn_jaw_ratio_default.set(0.5) self.avar_low._attr_inn_jaw_ratio_default.set(1.0) # # Add custom default connections # # Squeeze animator requested that the lips work like the animation mentor rig. # When the corner macros ud is 1.0, they won't follow the jaw anymore. # When the corner macros ud is -1.0, they won't follow the head anymore. avar_micro_corner_l = self.get_avar_l_corner() avar_micro_corner_r = self.get_avar_r_corner() libRigging.connectAttr_withLinearDrivenKeys( self.avar_l.attr_ud, avar_micro_corner_l._attr_inn_jaw_ratio_default, kt=(1.0, 0.0, -1.0), kv=(0.0, 0.5, 1.0), kit=(2, 2, 2), kot=(2, 2, 2), pre='linear', pst='linear' ) libRigging.connectAttr_withLinearDrivenKeys( self.avar_r.attr_ud, avar_micro_corner_r._attr_inn_jaw_ratio_default, kt=(1.0, 0.0, -1.0), kv=(0.0, 0.5, 1.0), kit=(2, 2, 2), kot=(2, 2, 2), pre='linear', pst='linear' ) # Ensure that the all macro avar bypass the jaw splitter as we expect it to be 100% linear. # todo: use another class for the 'all' macro avar. if self.create_macro_all: self.avar_all._attr_bypass_splitter.set(1.0) # Calibration is done manually since we need to setup the jaw influence. if calibrate: self.calibrate()
def connect_macro_avar(self, avar_macro, avar_micros): for avar_micro in avar_micros: libRigging.connectAttr_withLinearDrivenKeys( avar_macro.attr_ud, avar_micro.attr_ud) libRigging.connectAttr_withLinearDrivenKeys( avar_macro.attr_lr, avar_micro.attr_lr) libRigging.connectAttr_withLinearDrivenKeys( avar_macro.attr_fb, avar_micro.attr_fb) libRigging.connectAttr_withLinearDrivenKeys( avar_macro.attr_pt, avar_micro.attr_pt) # Add default FB avars to 'fake' a better lip curl pivot. # see: Art of Moving Points page 146 libRigging.connectAttr_withLinearDrivenKeys(avar_macro.attr_pt, avar_micro.attr_ud, kv=[0.01, 0.0, -0.01]) libRigging.connectAttr_withLinearDrivenKeys(avar_macro.attr_pt, avar_micro.attr_fb, kv=[0.01, 0.0, -0.01])
def _build_avars(self, **kwargs): super(FaceNose, self)._build_avars(**kwargs) # Create a ctrl that will control the whole nose ref = self.inf_nose_low if not self.avar_main: #self.avar_main = self.create_avar_macro(rig, self._CLS_CTRL, ref, name=self.name) self.avar_main = self._create_avar(ref, cls_ctrl=self._CLS_CTRL, name=self.name) self._build_avar_macro(self._CLS_CTRL, self.avar_main) ''' ctrl_upp_name = nomenclature_anm.resolve() if not isinstance(self.ctrl_main, self._CLS_CTRL): self.ctrl_main = self._CLS_CTRL() self.ctrl_main.build(rig, self.inf_nose_low, name=ctrl_upp_name) #self.create_ctrl_macro(rig, self.ctrl_main, self.inf_nose_low) ''' for avar in self.avars: libRigging.connectAttr_withLinearDrivenKeys( self.avar_main.attr_ud, avar.attr_ud) libRigging.connectAttr_withLinearDrivenKeys( self.avar_main.attr_lr, avar.attr_lr) libRigging.connectAttr_withLinearDrivenKeys( self.avar_main.attr_fb, avar.attr_fb) if self.avar_nose_upp: libRigging.connectAttr_withLinearDrivenKeys( self.avar_main.attr_pt, self.avar_nose_upp.attr_pt) libRigging.connectAttr_withLinearDrivenKeys( self.avar_main.attr_rl, self.avar_nose_upp.attr_rl) if self.avar_nose_low: libRigging.connectAttr_withLinearDrivenKeys( self.avar_main.attr_yw, self.avar_nose_low.attr_yw) if self.parent: pymel.parentConstraint(self.parent, self.avar_nose_upp._grp_offset, maintainOffset=True) nose_upp_out = self.avar_nose_upp._stack.node for avar in self.avars: if avar is self.avar_nose_upp: continue avar_inn = avar._grp_offset pymel.parentConstraint(nose_upp_out, avar_inn, maintainOffset=True)
def connect_macro_avar(self, avar_macro, avar_micros): for avar_micro in avar_micros: libRigging.connectAttr_withLinearDrivenKeys(avar_macro.attr_ud, avar_micro.attr_ud) libRigging.connectAttr_withLinearDrivenKeys(avar_macro.attr_lr, avar_micro.attr_lr) libRigging.connectAttr_withLinearDrivenKeys(avar_macro.attr_fb, avar_micro.attr_fb) libRigging.connectAttr_withLinearDrivenKeys(avar_macro.attr_pt, avar_micro.attr_pt) # Add default FB avars to 'fake' a better lip curl pivot. # see: Art of Moving Points page 146 libRigging.connectAttr_withLinearDrivenKeys(avar_macro.attr_pt, avar_micro.attr_ud, kv=[0.01, 0.0, -0.01]) libRigging.connectAttr_withLinearDrivenKeys(avar_macro.attr_pt, avar_micro.attr_fb, kv=[0.01, 0.0, -0.01])