def create_interface(self): super(ModelInteractiveCtrl, self).create_interface() # The values will be computed when attach_ctrl will be called libAttr.addAttr_separator( self.grp_rig, "ctrlCalibration" ) self.attr_sensitivity_tx = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TX, defaultValue=1.0 ) self.attr_sensitivity_ty = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TY, defaultValue=1.0 ) self.attr_sensitivity_tz = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TZ, defaultValue=1.0 ) self.attr_sensitivity_tx.set(channelBox=True) self.attr_sensitivity_ty.set(channelBox=True) self.attr_sensitivity_tz.set(channelBox=True)
def create_interface(self): super(CtrlModelCalibratable, self).create_interface() # Add sensitivity attributes on the ctrl. # The values will be adjusted on calibration. libAttr.addAttr_separator( self.grp_rig, "ctrlCalibration" ) self.attr_sensitivity_tx = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TX, defaultValue=1.0 ) self.attr_sensitivity_ty = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TY, defaultValue=1.0 ) self.attr_sensitivity_tz = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TZ, defaultValue=1.0 ) self.attr_sensitivity_tx.set(channelBox=True) self.attr_sensitivity_ty.set(channelBox=True) self.attr_sensitivity_tz.set(channelBox=True)
def post_buid_module(self, module): super(RigSqueeze, self).post_buid_module(module) # # Connect all IK/FK attributes # TODO: Ensure all attributes are correctly transfered # if isinstance(module, rigLimb.Limb): # Inverse IK/FK state. # At Squeeze, 0 is IK and 1 is FK, strange. module.STATE_IK = 0.0 module.STATE_FK = 1.0 pymel.delete(module.ctrl_attrs) module.ctrl_attrs = None # Resolve name # TODO: Handle name conflict nomenclature_anm = module.get_nomenclature_anm(self) nomenclature_attr = self.nomenclature(tokens=[module.__class__.__name__], side=nomenclature_anm.side) attr_src_name = nomenclature_attr.resolve() attr_dst = module.grp_rig.attr(module.kAttrName_State) if not self.grp_anm.hasAttr(self.GROUP_NAME_IKFK, checkShape=False): libAttr.addAttr_separator(self.grp_anm, self.GROUP_NAME_IKFK) attr_src = None if not self.grp_anm.hasAttr(attr_src_name, checkShape=False): attr_src = libAttr.addAttr(self.grp_anm, longName=attr_src_name, at='short', k=True, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, defaultValue=0) else: attr_src = self.grp_anm.attr(attr_src_name) # Note that at Squeeze, 0 is for IK and 1 is for FK so we'll need to reverse it. attr_src_inv = libRigging.create_utility_node('reverse', inputX=attr_src).outputX pymel.connectAttr(attr_src_inv, attr_dst) # # Set ctrls colors # color_by_side = { self.nomenclature.SIDE_L: 13, # Red self.nomenclature.SIDE_R: 6 # Blue } epsilon = 0.1 if module.grp_anm: nomenclature_anm = module.get_nomenclature_anm(self) for ctrl in module.get_ctrls(recursive=True): nomenclature_ctrl = nomenclature_anm.rebuild(ctrl.name()) side = nomenclature_ctrl.side color = color_by_side.get(side, None) if color: ctrl.drawOverride.overrideEnabled.set(1) ctrl.drawOverride.overrideColor.set(color)
def add_avars(self, attr_holder): """ Create the network that contain all our avars. For ease of use, the avars are exposed on the grp_rig, however to protect the connection from Maya when unbuilding they are really existing in an external network node. """ # Define macro avars libAttr.addAttr_separator(attr_holder, 'avars') self.attr_ud = self.add_avar(attr_holder, self.AVAR_NAME_UD) self.attr_lr = self.add_avar(attr_holder, self.AVAR_NAME_LR) self.attr_fb = self.add_avar(attr_holder, self.AVAR_NAME_FB) self.attr_yw = self.add_avar(attr_holder, self.AVAR_NAME_YAW) self.attr_pt = self.add_avar(attr_holder, self.AVAR_NAME_PITCH) self.attr_rl = self.add_avar(attr_holder, self.AVAR_NAME_ROLL)
def create_stacks(self): super(FaceLipsAvar, self).create_stacks() nomenclature_rig = self.get_nomenclature_rig() jnt_jaw = self.get_jaw_jnt() jaw_module = self.get_jaw_module() # # Create additional attributes to control the jaw layer # libAttr.addAttr_separator(self.grp_rig, 'jawLayer') self._attr_inn_jaw_ratio_default = libAttr.addAttr( self.grp_rig, 'jawRatioDefault', defaultValue=0.5, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, k=True ) self._attr_bypass_splitter = libAttr.addAttr( self.grp_rig, 'jawSplitterBypass', defaultValue=0.0, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, k=True ) # self._target_jaw_bindpose = self._parent_module._ref_jaw_predeform # self._attr_jaw_pitch = self._parent_module._attr_jaw_pt # Variable shared with the AvarInflModel self._attr_jaw_bind_tm = self._parent_module._ref_jaw_predeform.matrix self._attr_jaw_pitch = self._parent_module._attr_jaw_pt
def pre_build(self): super(RigSqueeze, self).pre_build(create_master_grp=False) # # Create specific group related to squeeze rig convention # all_geos = libPymel.ls_root_geos() # Build All_Grp self.grp_master = self.build_grp(classRig.RigGrp, self.grp_master, self.nomenclature.root_all_name) self.grp_model = self.build_grp(classRig.RigGrp, self.grp_model, self.nomenclature.root_model_name) self.grp_proxy = self.build_grp(classRig.RigGrp, self.grp_proxy, self.nomenclature.root_proxy_name) self.grp_fx = self.build_grp(classRig.RigGrp, self.grp_fx, self.nomenclature.root_fx_name) # Parent all groups in the main grp_master pymel.parent(self.grp_anm, self.grp_master) # grp_anm is not a Node, but a Ctrl self.grp_rig.setParent(self.grp_master) self.grp_fx.setParent(self.grp_master) self.grp_model.setParent(self.grp_master) self.grp_proxy.setParent(self.grp_master) self.grp_geo.setParent(self.grp_master) ''' if self.grp_jnt.getParent() is None: self.grp_jnt.setParent(self.grp_master) ''' # Lock and hide all attributes we don't want the animator to play with libAttr.lock_hide_trs(self.grp_master) libAttr.lock_hide_trs(self.grp_rig) libAttr.lock_hide_trs(self.grp_fx) libAttr.lock_hide_trs(self.grp_model) libAttr.lock_hide_trs(self.grp_proxy) libAttr.lock_hide_trs(self.grp_geo) libAttr.hide_scale(self.grp_anm) # Hide some group # self.grp_jnt.visibility.set(False) self.grp_rig.visibility.set(False) self.grp_fx.visibility.set(False) self.grp_model.visibility.set(False) # # Add root ctrl attributes specific to squeeze # if not self.grp_anm.hasAttr(self.GROUP_NAME_DISPLAY, checkShape=False): libAttr.addAttr_separator(self.grp_anm, self.GROUP_NAME_DISPLAY) # Display Mesh if not self.grp_anm.hasAttr(self.ATTR_NAME_DISPLAY_MESH, checkShape=False): attr_displayMesh = libAttr.addAttr(self.grp_anm, longName=self.ATTR_NAME_DISPLAY_MESH, at='short', k=True, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, defaultValue=1) else: attr_displayMesh = self.grp_anm.attr(self.ATTR_NAME_DISPLAY_MESH) # Display Ctrl if not self.grp_anm.hasAttr(self.ATTR_NAME_DISPLAY_CTRL, checkShape=False): attr_displayCtrl = libAttr.addAttr(self.grp_anm, longName=self.ATTR_NAME_DISPLAY_CTRL, at='short', k=True, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, defaultValue=1) else: attr_displayCtrl = self.grp_anm.attr(self.ATTR_NAME_DISPLAY_CTRL) # Display Proxy if not self.grp_anm.hasAttr(self.ATTR_NAME_DISPLAY_PROXY, checkShape=False): attr_displayProxy = libAttr.addAttr(self.grp_anm, longName=self.ATTR_NAME_DISPLAY_PROXY, at='short', k=True, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, defaultValue=0) else: attr_displayProxy = self.grp_anm.attr(self.ATTR_NAME_DISPLAY_PROXY) pymel.connectAttr(attr_displayMesh, self.grp_geo.visibility, force=True) pymel.connectAttr(attr_displayProxy, self.grp_proxy.visibility, force=True) for child in self.grp_anm.getChildren(): pymel.connectAttr(attr_displayCtrl, child.visibility, force=True)
def build(self, constraint=True, constraint_handle=True, setup_softik=True, **kwargs): """ :param constraint: Bool to tell if we will constraint the chain bone on the ikchain :param constraint_handle: Bool to tell if we will contraint the handle on the ik ctrl :param setup_softik: Bool to tell if we setup the soft ik system :param kwargs: More kwargs passed to the superclass :return: Nothing """ # Build the softik node after the setup for the quadruped super(LegIkQuad, self).build(constraint=False, constraint_handle=False, setup_softik=False, **kwargs) nomenclature_rig = self.get_nomenclature_rig() quad_swivel_pos = self.calc_swivel_pos(start_index=1, end_index=3) heel_idx = self.iCtrlIndex - 1 # Create a second ik chain for the quadruped setup self._chain_quad_ik = self.chain.duplicate() for i, oIk in enumerate(self._chain_quad_ik): oIk.rename(nomenclature_rig.resolve('QuadChain{0:02}'.format(i))) # Constraint the bones after the iCtrlIdx to the first ik chain to make the foot roll work correctly if i > self.iCtrlIndex: pymel.parentConstraint(self._chain_ik[i], self._chain_quad_ik[i]) self._chain_quad_ik[0].setParent(self._chain_ik[0]) obj_e = self._chain_quad_ik[self.iCtrlIndex] # We need a second ik solver for the quad chain ik_solver_quad_name = nomenclature_rig.resolve('quadIkHandle') ik_effector_quad_name = nomenclature_rig.resolve('quadIkEffector') self._ik_handle_quad, _ik_effector = pymel.ikHandle(startJoint=self._chain_quad_ik[1], endEffector=obj_e, solver='ikRPsolver') self._ik_handle_quad.rename(ik_solver_quad_name) _ik_effector.rename(ik_effector_quad_name) self._ik_handle_quad.setParent(self._ik_handle) # # Create softIk node and connect user accessible attributes to it. # if setup_softik: self.setup_softik([self._ik_handle, self._ik_handle_quad], self._chain_quad_ik) # Create another swivel handle node for the quad chain setup self.ctrl_swivel_quad = self.setup_swivel_ctrl(self.ctrl_swivel_quad, self._chain_quad_ik[heel_idx], quad_swivel_pos, self._ik_handle_quad, name='swivelQuad', mirror_setup=False) self.quad_swivel_distance = self.chain_length # Used in ik/fk switch # Set by default the space to calf if self.ctrl_swivel_quad.space: enum = self.ctrl_swivel_quad.space.getEnums() calf_idx = enum.get('Calf', None) if calf_idx: self.ctrl_swivel_quad.space.set(calf_idx) attr_holder = self.ctrl_ik libAttr.addAttr_separator(attr_holder, 'Quadruped', niceName='Quadruped') attr_pitch = libAttr.addAttr(attr_holder, longName='pitch', k=True) pymel.connectAttr(attr_pitch, self._chain_quad_ik[0].rotateZ) pymel.orientConstraint(self.ctrl_ik, obj_e, maintainOffset=True) if constraint: for source, target in zip(self._chain_quad_ik, self.chain): pymel.parentConstraint(source, target)
def build(self, avar, ref=None, ref_tm=None, grp_rig=None, obj_mesh=None, u_coord=None, v_coord=None, flip_lr=False, follow_mesh=True, ctrl_tm=None, ctrl_size=1.0, parent_pos=None, parent_rot=None, parent_scl=None, constraint=False, cancel_t=True, cancel_r=True, attr_bind_tm=None, **kwargs): # todo: get rid of the u_coods, v_coods etc, we should rely on the bind super(ModelCtrlLinear, self).build(avar, ctrl_size=ctrl_size, **kwargs) nomenclature_rig = self.get_nomenclature_rig() # # Resolve necessary informations # # Resolve which object will the InteractiveCtrl track. # If we don't want to follow a particular geometry, we'll use the end of the stack. # Otherwise the influence will be used (to also resolve the geometry). # todo: it could be better to resolve the geometry ourself if ref is None: ref = self.jnt # Resolve the ctrl default tm if ctrl_tm is None: ctrl_tm = self.get_default_tm_ctrl() if ctrl_tm is None: raise Exception("Cannot resolve ctrl transformation matrix!") # By default, we expect the rigger to mirror the face joints using the 'behavior' mode. if flip_lr: ctrl_tm = pymel.datatypes.Matrix( 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0) * ctrl_tm self._grp_bind_ctrl = pymel.createNode( 'transform', name=nomenclature_rig.resolve('ctrlBindTm'), parent=self.grp_rig, ) self._grp_bind_ctrl.setMatrix(ctrl_tm) # Resolve the influence bind tm # Create an offset node to easily change it. self._grp_bind_infl = pymel.createNode('transform', name=nomenclature_rig.resolve('bind'), parent=self.grp_rig) if attr_bind_tm: util_decompose_bind = libRigging.create_utility_node( 'decomposeMatrix', inputMatrix=attr_bind_tm, ) pymel.connectAttr(util_decompose_bind.outputTranslate, self._grp_bind_infl.translate) pymel.connectAttr(util_decompose_bind.outputRotate, self._grp_bind_infl.rotate) pymel.connectAttr(util_decompose_bind.outputScale, self._grp_bind_infl.scale) else: # todo: deprecate this? self._grp_bind_infl.setTranslation(ctrl_tm.translate) # Create a follicle, this will be used for calibration purpose. # If this affect performance we can create it only when necessary, however being able to # see it help with debugging. follicle_transform, follicle_shape = self._create_follicle( ctrl_tm, ref, obj_mesh=obj_mesh, u_coord=u_coord, v_coord=v_coord, ) self.follicle = follicle_transform # # Add calibration-related attribute # # The values will be computed when attach_ctrl will be called libAttr.addAttr_separator( self.grp_rig, "ctrlCalibration" ) self.attr_sensitivity_tx = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TX, defaultValue=1.0 ) self.attr_sensitivity_ty = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TY, defaultValue=1.0 ) self.attr_sensitivity_tz = libAttr.addAttr( self.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TZ, defaultValue=1.0 ) self.attr_sensitivity_tx.set(channelBox=True) self.attr_sensitivity_ty.set(channelBox=True) self.attr_sensitivity_tz.set(channelBox=True) # Hack: Since there's scaling on the ctrl so the left and right side ctrl channels matches, we need to flip the ctrl shapes. if flip_lr: self.ctrl.scaleX.set(-1) libPymel.makeIdentity_safe(self.ctrl, rotate=True, scale=True, apply=True) grp_output = pymel.createNode( 'transform', name=nomenclature_rig.resolve('output'), parent=self.grp_rig ) attr_output_tm = libRigging.create_utility_node( 'multMatrix', matrixIn=( self._grp_bind_ctrl.matrix, self._attr_inn_parent_tm, self.rig.grp_anm.worldInverseMatrix ) ).matrixSum libRigging.connect_matrix_to_node(attr_output_tm, grp_output) # Create inverted attributes for sensibility util_sensitivity_inv = libRigging.create_utility_node( 'multiplyDivide', operation=2, input1X=1.0, input1Y=1.0, input1Z=1.0, input2X=self.attr_sensitivity_tx, input2Y=self.attr_sensitivity_ty, input2Z=self.attr_sensitivity_tz ) attr_sensibility_lr_inv = util_sensitivity_inv.outputX attr_sensibility_ud_inv = util_sensitivity_inv.outputY attr_sensibility_fb_inv = util_sensitivity_inv.outputZ attr_ctrl_offset_sx_inn = self.attr_sensitivity_tx attr_ctrl_offset_sy_inn = self.attr_sensitivity_ty attr_ctrl_offset_sz_inn = self.attr_sensitivity_tz # Connect any additionel scale source. if parent_scl: u = libRigging.create_utility_node( 'multiplyDivide', name=nomenclature_rig.resolve('getAbsoluteCtrlOffsetScale'), input1X=attr_ctrl_offset_sx_inn, input1Y=attr_ctrl_offset_sy_inn, input1Z=attr_ctrl_offset_sz_inn, input2X=parent_scl.scaleX, input2Y=parent_scl.scaleY, input2Z=parent_scl.scaleZ ) attr_ctrl_offset_sx_inn, attr_ctrl_offset_sy_inn, attr_ctrl_offset_sz_inn = u.outputX, u.outputY, u.outputZ # Ensure the scaling of the parent is taken in account. attr_calibration_scale_tm = libRigging.create_utility_node( 'composeMatrix', name=nomenclature_rig.resolve('composeCalibrationScaleTm'), inputScaleX=attr_ctrl_offset_sx_inn, inputScaleY=attr_ctrl_offset_sy_inn, inputScaleZ=attr_ctrl_offset_sz_inn, ).outputMatrix attr_ctrl_offset_scale_tm = libRigging.create_utility_node( 'multMatrix', name=nomenclature_rig.resolve('getCtrlOffsetScaleTm'), matrixIn=( attr_calibration_scale_tm, self._attr_inn_parent_tm, self.rig.grp_anm.worldInverseMatrix, ) ).matrixSum attr_ctrl_offset_scale = libRigging.create_utility_node( 'decomposeMatrix', inputMatrix=attr_ctrl_offset_scale_tm ).outputScale # Flip the x axis if we are on the right side of the face. # We need to do it as the last step since this will result in a right-handed matrix # which will be canceled out if we feed it into multMatrix or other maya nodes. if flip_lr: attr_ctrl_offset_scale = libRigging.create_utility_node( 'multiplyDivide', input1=attr_ctrl_offset_scale, input2X=-1.0, input2Y=1.0, input2Z=1.0, ).output pymel.connectAttr(attr_ctrl_offset_scale, self.ctrl.offset.scale) # Apply sensibility on the ctrl shape ctrl_shape = self.ctrl.node.getShape() tmp = pymel.duplicate(self.ctrl.node.getShape())[0] ctrl_shape_orig = tmp.getShape() ctrl_shape_orig.setParent(self.ctrl.node, relative=True, shape=True) ctrl_shape_orig.rename('{0}Orig'.format(ctrl_shape.name())) pymel.delete(tmp) ctrl_shape_orig.intermediateObject.set(True) for cp in ctrl_shape.cp: cp.set(0, 0, 0) # Counter-scale the shape attr_adjustement_sx_inn = attr_sensibility_lr_inv attr_adjustement_sy_inn = attr_sensibility_ud_inv attr_adjustement_sz_inn = attr_sensibility_fb_inv attr_adjustement_scale = libRigging.create_utility_node( 'composeMatrix', inputScaleX=attr_adjustement_sx_inn, inputScaleY=attr_adjustement_sy_inn, inputScaleZ=attr_adjustement_sz_inn ).outputMatrix attr_adjustement_rot = libRigging.create_utility_node( 'composeMatrix', inputRotateX=self.ctrl.node.rotateX, inputRotateY=self.ctrl.node.rotateY, inputRotateZ=self.ctrl.node.rotateZ ).outputMatrix attr_adjustement_rot_inv = libRigging.create_utility_node( 'inverseMatrix', inputMatrix=attr_adjustement_rot ).outputMatrix attr_adjustement_tm = libRigging.create_utility_node( 'multMatrix', matrixIn=[ attr_adjustement_rot, attr_adjustement_scale, attr_adjustement_rot_inv ] ).matrixSum attr_transform_geometry = libRigging.create_utility_node( 'transformGeometry', transform=attr_adjustement_tm, inputGeometry=ctrl_shape_orig.local ).outputGeometry pymel.connectAttr(attr_transform_geometry, ctrl_shape.create, force=True) pymel.connectAttr(grp_output.translate, self.ctrl.offset.translate) pymel.connectAttr(grp_output.rotate, self.ctrl.offset.rotate) if constraint and self.jnt: pymel.parentConstraint(self.ctrl.node, self.jnt, maintainOffset=True)
def build(self, constraint=True, constraint_handle=True, setup_softik=True, **kwargs): """ :param constraint: Bool to tell if we will constraint the chain bone on the ikchain :param constraint_handle: Bool to tell if we will contraint the handle on the ik ctrl :param setup_softik: Bool to tell if we setup the soft ik system :param kwargs: More kwargs passed to the superclass :return: Nothing """ # Build the softik node after the setup for the quadruped super(LegIkQuad, self).build(constraint=False, constraint_handle=False, setup_softik=False, **kwargs) nomenclature_rig = self.get_nomenclature_rig() quad_swivel_pos = self.calc_swivel_pos(start_index=1, end_index=3) heel_idx = self.iCtrlIndex - 1 # Create a second ik chain for the quadruped setup self._chain_quad_ik = self.chain.duplicate() for i, oIk in enumerate(self._chain_quad_ik): oIk.rename(nomenclature_rig.resolve('QuadChain{0:02}'.format(i))) # Constraint the bones after the iCtrlIdx to the first ik chain to make the foot roll work correctly if i > self.iCtrlIndex: pymel.parentConstraint(self._chain_ik[i], self._chain_quad_ik[i]) self._chain_quad_ik[0].setParent(self._chain_ik[0]) obj_e = self._chain_quad_ik[self.iCtrlIndex] # We need a second ik solver for the quad chain ik_solver_quad_name = nomenclature_rig.resolve('quadIkHandle') ik_effector_quad_name = nomenclature_rig.resolve('quadIkEffector') self._ik_handle_quad, _ik_effector = pymel.ikHandle( startJoint=self._chain_quad_ik[1], endEffector=obj_e, solver='ikRPsolver') self._ik_handle_quad.rename(ik_solver_quad_name) _ik_effector.rename(ik_effector_quad_name) self._ik_handle_quad.setParent(self._ik_handle) # # Create softIk node and connect user accessible attributes to it. # if setup_softik: self.setup_softik([self._ik_handle, self._ik_handle_quad], self._chain_quad_ik) # Create another swivel handle node for the quad chain setup self.ctrl_swivel_quad = self.setup_swivel_ctrl( self.ctrl_swivel_quad, self._chain_quad_ik[heel_idx], quad_swivel_pos, self._ik_handle_quad, name='swivelQuad', mirror_setup=False) self.quad_swivel_distance = self.chain_length # Used in ik/fk switch # Set by default the space to calf if self.ctrl_swivel_quad.space: enum = self.ctrl_swivel_quad.space.getEnums() calf_idx = enum.get('Calf', None) if calf_idx: self.ctrl_swivel_quad.space.set(calf_idx) attr_holder = self.ctrl_ik libAttr.addAttr_separator(attr_holder, 'Quadruped', niceName='Quadruped') attr_pitch = libAttr.addAttr(attr_holder, longName='pitch', k=True) pymel.connectAttr(attr_pitch, self._chain_quad_ik[0].rotateZ) pymel.orientConstraint(self.ctrl_ik, obj_e, maintainOffset=True) if constraint: for source, target in zip(self._chain_quad_ik, self.chain): pymel.parentConstraint(source, target)
def build(self, module, ref, ref_tm=None, grp_rig=None, obj_mesh=None, u_coord=None, v_coord=None, flip_lr=False, follow_mesh=True, **kwargs): """ Create an Interactive controller that follow a geometry. :param module: ??? :param ref: :param ref_tm: :param grp_rig: :param obj_mesh: :param u_coord: :param v_coord: :param kwargs: :return: """ # HACK: Ensure flipped shapes are correctly restaured... # This is necessary since when holded, the scale of the ctrl is set to identity. # However ctrl from the right side have an inverted scale on the x axis. -_- if flip_lr and libPymel.is_valid_PyNode(self.shapes): self.shapes.sx.set(-1) pymel.makeIdentity(self.shapes, rotate=True, scale=True, apply=True) # todo: Simplify the setup, too many nodes super(InteractiveCtrl, self).build(**kwargs) #nomenclature_anm = self.get_nomenclature_anm(parent) nomenclature_rig = module.rig.nomenclature(suffix=module.rig.nomenclature.type_rig) #nomenclature_rig = self.get_nomenclature_rig(parent) # TODO: Only use position instead of PyNode or Matrix? if ref_tm is None: ref_tm = ref.getMatrix(worldSpace=True) pos_ref = ref_tm.translate # Resolve u and v coordinates # todo: check if we really want to resolve the u and v ourself since it's now connected. if obj_mesh is None: # We'll scan all available geometries and use the one with the shortest distance. meshes = libRigging.get_affected_geometries(ref) meshes = list(set(meshes) & set(module.rig.get_meshes())) obj_mesh, _, out_u, out_v = libRigging.get_closest_point_on_shapes(meshes, pos_ref) else: _, out_u, out_v = libRigging.get_closest_point_on_shape(obj_mesh, pos_ref) if u_coord is None: u_coord = out_u if v_coord is None: v_coord = out_v if obj_mesh is None: raise Exception("Can't find mesh affected by {0}. Skipping doritos ctrl setup.") if self.jnt: module.debug('Creating doritos on {0} using {1} as reference'.format(obj_mesh, self.jnt)) else: module.debug('Creating doritos on {0}'.format(obj_mesh)) # Initialize external stack # Normally this would be hidden from animators. stack_name = nomenclature_rig.resolve('doritosStack') stack = classNode.Node(self) stack.build(name=stack_name) stack.setTranslation(pos_ref) # Add sensibility attributes # The values will be computed when attach_ctrl will be called libAttr.addAttr_separator( module.grp_rig, "ctrlCalibration" ) self.attr_sensitivity_tx = libAttr.addAttr( module.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TX, defaultValue=1.0 ) self.attr_sensitivity_ty = libAttr.addAttr( module.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TY, defaultValue=1.0 ) self.attr_sensitivity_tz = libAttr.addAttr( module.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TZ, defaultValue=1.0 ) self.attr_sensitivity_tx.set(channelBox=True) self.attr_sensitivity_ty.set(channelBox=True) self.attr_sensitivity_tz.set(channelBox=True) # Note that to only check in the Z axis, we'll do a raycast first. # If we success this will become our reference position. ''' pos = pos_ref pos.z = 999 dir = pymel.datatypes.Point(0,0,-1) result = next(iter(libRigging.ray_cast(pos, dir, [obj_mesh])), None) if result: pos_ref = result ctrl_tm.translate = result ''' # Create the layer_fol that will follow the geometry layer_fol_name = nomenclature_rig.resolve('doritosFol') layer_fol = stack.append_layer() layer_fol.rename(layer_fol_name) #layer_fol.setParent(self.grp_rig) # TODO: Validate that we don't need to inverse the rotation separately. fol_mesh = None if follow_mesh: fol_name = nomenclature_rig.resolve('doritosFollicle') fol_shape = libRigging.create_follicle2(obj_mesh, u=u_coord, v=v_coord) fol_mesh = fol_shape.getParent() self.follicle = fol_mesh fol_mesh.rename(fol_name) pymel.parentConstraint(fol_mesh, layer_fol, maintainOffset=True) fol_mesh.setParent(self.grp_rig) # HACK: Fix rotation issues. # The doritos setup can be hard to control when the rotation of the controller depend on the layer_fol since # any deformation can affect the normal of the faces. jnt_head = module.rig.get_head_jnt() if jnt_head: pymel.disconnectAttr(layer_fol.rx) pymel.disconnectAttr(layer_fol.ry) pymel.disconnectAttr(layer_fol.rz) pymel.orientConstraint(jnt_head, layer_fol, maintainOffset=True) else: self.follicle = layer_fol pymel.parentConstraint(ref, layer_fol, maintainOffset=True) # # Constraint a specic controller to the avar doritos stack. # Call this method after connecting the ctrl to the necessary avars. # The sensibility of the doritos will be automatically computed in this step if necessary. # # Create inverted attributes for sensibility util_sensitivity_inv = libRigging.create_utility_node('multiplyDivide', operation=2, input1X=1.0, input1Y=1.0, input1Z=1.0, input2X=self.attr_sensitivity_tx, input2Y=self.attr_sensitivity_ty, input2Z=self.attr_sensitivity_tz ) attr_sensibility_lr_inv = util_sensitivity_inv.outputX attr_sensibility_ud_inv = util_sensitivity_inv.outputY attr_sensibility_fb_inv = util_sensitivity_inv.outputZ # Add an inverse node that will counter animate the position of the ctrl. # TODO: Rename layer_doritos_name = nomenclature_rig.resolve('doritosInv') layer_doritos = pymel.createNode('transform', name=layer_doritos_name) layer_doritos.setParent(stack.node) # Create inverse attributes for the ctrl attr_ctrl_inv_t = libRigging.create_utility_node('multiplyDivide', input1=self.node.t, input2=[-1, -1, -1]).output attr_ctrl_inv_r = libRigging.create_utility_node('multiplyDivide', input1=self.node.r, input2=[-1, -1, -1]).output attr_ctrl_inv_t = libRigging.create_utility_node('multiplyDivide', input1=attr_ctrl_inv_t, input2X=self.attr_sensitivity_tx, input2Y=self.attr_sensitivity_ty, input2Z=self.attr_sensitivity_tz ).output if flip_lr: attr_doritos_tx = libRigging.create_utility_node('multiplyDivide', input1X=attr_ctrl_inv_t.outputX, input2X=-1 ).outputX else: attr_doritos_tx = attr_ctrl_inv_t.outputX attr_doritos_ty = attr_ctrl_inv_t.outputY attr_doritos_tz = attr_ctrl_inv_t.outputZ pymel.connectAttr(attr_doritos_tx, layer_doritos.tx) pymel.connectAttr(attr_doritos_ty, layer_doritos.ty) pymel.connectAttr(attr_doritos_tz, layer_doritos.tz) pymel.connectAttr(attr_ctrl_inv_r, layer_doritos.r) # Apply scaling on the ctrl parent. # This is were the 'black magic' happen. if flip_lr: attr_ctrl_offset_sx_inn = libRigging.create_utility_node('multiplyDivide', input1X=self.attr_sensitivity_tx, input2X=-1 ).outputX else: attr_ctrl_offset_sx_inn = self.attr_sensitivity_tx attr_ctrl_offset_sy_inn = self.attr_sensitivity_ty attr_ctrl_offset_sz_inn = self.attr_sensitivity_tz pymel.connectAttr(attr_ctrl_offset_sx_inn, self.offset.scaleX) pymel.connectAttr(attr_ctrl_offset_sy_inn, self.offset.scaleY) pymel.connectAttr(attr_ctrl_offset_sz_inn, self.offset.scaleZ) # Apply sensibility on the ctrl shape ctrl_shape = self.node.getShape() tmp = pymel.duplicate(self.node.getShape())[0] ctrl_shape_orig = tmp.getShape() ctrl_shape_orig.setParent(self.node, relative=True, shape=True) ctrl_shape_orig.rename('{0}Orig'.format(ctrl_shape.name())) pymel.delete(tmp) ctrl_shape_orig.intermediateObject.set(True) for cp in ctrl_shape.cp: cp.set(0,0,0) # Counter-scale the shape attr_adjustement_sx_inn = attr_sensibility_lr_inv attr_adjustement_sy_inn = attr_sensibility_ud_inv attr_adjustement_sz_inn = attr_sensibility_fb_inv attr_adjustement_scale = libRigging.create_utility_node('composeMatrix', inputScaleX=attr_adjustement_sx_inn, inputScaleY=attr_adjustement_sy_inn, inputScaleZ=attr_adjustement_sz_inn ).outputMatrix attr_adjustement_rot = libRigging.create_utility_node('composeMatrix', inputRotateX=self.node.rotateX, inputRotateY=self.node.rotateY, inputRotateZ=self.node.rotateZ ).outputMatrix attr_adjustement_rot_inv = libRigging.create_utility_node('inverseMatrix', inputMatrix=attr_adjustement_rot).outputMatrix attr_adjustement_tm = libRigging.create_utility_node('multMatrix', matrixIn=[ attr_adjustement_rot, attr_adjustement_scale, attr_adjustement_rot_inv ]).matrixSum attr_transform_geometry = libRigging.create_utility_node('transformGeometry', transform=attr_adjustement_tm, inputGeometry=ctrl_shape_orig.local).outputGeometry pymel.connectAttr(attr_transform_geometry, ctrl_shape.create, force=True) # Constraint ctrl pymel.parentConstraint(layer_doritos, self.offset, maintainOffset=False, skipRotate=['x', 'y', 'z']) pymel.orientConstraint(layer_doritos.getParent(), self.offset, maintainOffset=True) # Clean dag junk if grp_rig: stack.setParent(grp_rig) if fol_mesh: fol_mesh.setParent(grp_rig)
def build_stack(self, stack, **kwargs): nomenclature_rig = self.get_nomenclature_rig() jnt_head = self.rig.get_head_jnt() jnt_jaw = self.rig.get_jaw_jnt() # # Create additional attributes to control the jaw layer # libAttr.addAttr_separator(self.grp_rig, 'jawLayer') self._attr_inn_jaw_ratio_default = libAttr.addAttr(self.grp_rig, 'jawRatioDefault', defaultValue=0.5, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, k=True) self._attr_bypass_splitter = libAttr.addAttr(self.grp_rig, 'jawSplitterBypass', defaultValue=0.0, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, k=True) # # Create reference objects used for calculations. # # Create a reference node that follow the head self._target_head = pymel.createNode( 'transform', name=nomenclature_rig.resolve('innHead'), parent=self.grp_rig) self._target_head.setTranslation( jnt_head.getTranslation(space='world')) pymel.parentConstraint(jnt_head, self._target_head, maintainOffset=True) pymel.scaleConstraint(jnt_head, self._target_head, maintainOffset=True) # Create a reference node that follow the jaw initial position jaw_pos = jnt_jaw.getTranslation(space='world') self._target_jaw_bindpose = pymel.createNode( 'transform', name=nomenclature_rig.resolve('innJawBindPose'), parent=self.grp_rig) self._target_jaw_bindpose.setTranslation(jaw_pos) # Create a reference node that follow the jaw self._target_jaw = pymel.createNode( 'transform', name=nomenclature_rig.resolve('innJaw'), parent=self._target_jaw_bindpose) self._target_jaw.t.set(0, 0, 0) pymel.parentConstraint(jnt_jaw, self._target_jaw, maintainOffset=True) pymel.scaleConstraint(jnt_jaw, self._target_jaw, maintainOffset=True) # Create a node that contain the out jaw influence. # Note that the out jaw influence can be modified by the splitter node. grp_parent_pos = self._grp_parent.getTranslation( space='world') # grp_offset is always in world coordinates self._jaw_ref = pymel.createNode( 'transform', name=nomenclature_rig.resolve('outJawInfluence'), parent=self.grp_rig) self._jaw_ref.t.set(grp_parent_pos) pymel.parentConstraint(self._target_jaw, self._jaw_ref, maintainOffset=True) # Extract jaw influence attr_delta_tm = libRigging.create_utility_node( 'multMatrix', matrixIn=[ self._jaw_ref.worldMatrix, self._grp_parent.worldInverseMatrix ]).matrixSum util_extract_jaw = libRigging.create_utility_node( 'decomposeMatrix', name=nomenclature_rig.resolve('getJawRotation'), inputMatrix=attr_delta_tm) super(FaceLipsAvar, self).build_stack(stack, **kwargs) # # Create jaw influence layer # Create a reference object to extract the jaw displacement. # # Add the jaw influence as a new stack layer. layer_jaw_r = stack.prepend_layer(name='jawRotate') layer_jaw_t = stack.prepend_layer(name='jawTranslate') pymel.connectAttr(util_extract_jaw.outputTranslate, layer_jaw_t.t) pymel.connectAttr(util_extract_jaw.outputRotate, layer_jaw_r.r)
def build(self, module, ref, ref_tm=None, grp_rig=None, obj_mesh=None, u_coord=None, v_coord=None, flip_lr=False, follow_mesh=True, **kwargs): """ Create an Interactive controller that follow a geometry. :param module: ??? :param ref: :param ref_tm: :param grp_rig: :param obj_mesh: :param u_coord: :param v_coord: :param kwargs: :return: """ # HACK: Ensure flipped shapes are correctly restaured... # This is necessary since when holded, the scale of the ctrl is set to identity. # However ctrl from the right side have an inverted scale on the x axis. -_- if flip_lr and libPymel.is_valid_PyNode(self.shapes): self.shapes.sx.set(-1) pymel.makeIdentity(self.shapes, rotate=True, scale=True, apply=True) # todo: Simplify the setup, too many nodes super(InteractiveCtrl, self).build(**kwargs) #nomenclature_anm = self.get_nomenclature_anm(parent) nomenclature_rig = module.rig.nomenclature( suffix=module.rig.nomenclature.type_rig) #nomenclature_rig = self.get_nomenclature_rig(parent) # TODO: Only use position instead of PyNode or Matrix? if ref_tm is None: ref_tm = ref.getMatrix(worldSpace=True) pos_ref = ref_tm.translate # Resolve u and v coordinates # todo: check if we really want to resolve the u and v ourself since it's now connected. if obj_mesh is None: # We'll scan all available geometries and use the one with the shortest distance. meshes = libRigging.get_affected_geometries(ref) meshes = list(set(meshes) & set(module.rig.get_meshes())) obj_mesh, _, out_u, out_v = libRigging.get_closest_point_on_shapes( meshes, pos_ref) else: _, out_u, out_v = libRigging.get_closest_point_on_shape( obj_mesh, pos_ref) if u_coord is None: u_coord = out_u if v_coord is None: v_coord = out_v if obj_mesh is None: raise Exception( "Can't find mesh affected by {0}. Skipping doritos ctrl setup." ) if self.jnt: module.debug( 'Creating doritos on {0} using {1} as reference'.format( obj_mesh, self.jnt)) else: module.debug('Creating doritos on {0}'.format(obj_mesh)) # Initialize external stack # Normally this would be hidden from animators. stack_name = nomenclature_rig.resolve('doritosStack') stack = classNode.Node(self) stack.build(name=stack_name) stack.setTranslation(pos_ref) # Add sensibility attributes # The values will be computed when attach_ctrl will be called libAttr.addAttr_separator(module.grp_rig, "ctrlCalibration") self.attr_sensitivity_tx = libAttr.addAttr( module.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TX, defaultValue=1.0) self.attr_sensitivity_ty = libAttr.addAttr( module.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TY, defaultValue=1.0) self.attr_sensitivity_tz = libAttr.addAttr( module.grp_rig, longName=self._ATTR_NAME_SENSITIVITY_TZ, defaultValue=1.0) self.attr_sensitivity_tx.set(channelBox=True) self.attr_sensitivity_ty.set(channelBox=True) self.attr_sensitivity_tz.set(channelBox=True) # Note that to only check in the Z axis, we'll do a raycast first. # If we success this will become our reference position. ''' pos = pos_ref pos.z = 999 dir = pymel.datatypes.Point(0,0,-1) result = next(iter(libRigging.ray_cast(pos, dir, [obj_mesh])), None) if result: pos_ref = result ctrl_tm.translate = result ''' # Create the layer_fol that will follow the geometry layer_fol_name = nomenclature_rig.resolve('doritosFol') layer_fol = stack.append_layer() layer_fol.rename(layer_fol_name) #layer_fol.setParent(self.grp_rig) # TODO: Validate that we don't need to inverse the rotation separately. fol_mesh = None if follow_mesh: fol_name = nomenclature_rig.resolve('doritosFollicle') fol_shape = libRigging.create_follicle2(obj_mesh, u=u_coord, v=v_coord) fol_mesh = fol_shape.getParent() self.follicle = fol_mesh fol_mesh.rename(fol_name) pymel.parentConstraint(fol_mesh, layer_fol, maintainOffset=True) fol_mesh.setParent(self.grp_rig) # HACK: Fix rotation issues. # The doritos setup can be hard to control when the rotation of the controller depend on the layer_fol since # any deformation can affect the normal of the faces. jnt_head = module.rig.get_head_jnt() if jnt_head: pymel.disconnectAttr(layer_fol.rx) pymel.disconnectAttr(layer_fol.ry) pymel.disconnectAttr(layer_fol.rz) pymel.orientConstraint(jnt_head, layer_fol, maintainOffset=True) else: self.follicle = layer_fol pymel.parentConstraint(ref, layer_fol, maintainOffset=True) # # Constraint a specic controller to the avar doritos stack. # Call this method after connecting the ctrl to the necessary avars. # The sensibility of the doritos will be automatically computed in this step if necessary. # # Create inverted attributes for sensibility util_sensitivity_inv = libRigging.create_utility_node( 'multiplyDivide', operation=2, input1X=1.0, input1Y=1.0, input1Z=1.0, input2X=self.attr_sensitivity_tx, input2Y=self.attr_sensitivity_ty, input2Z=self.attr_sensitivity_tz) attr_sensibility_lr_inv = util_sensitivity_inv.outputX attr_sensibility_ud_inv = util_sensitivity_inv.outputY attr_sensibility_fb_inv = util_sensitivity_inv.outputZ # Add an inverse node that will counter animate the position of the ctrl. # TODO: Rename layer_doritos_name = nomenclature_rig.resolve('doritosInv') layer_doritos = pymel.createNode('transform', name=layer_doritos_name) layer_doritos.setParent(stack.node) # Create inverse attributes for the ctrl attr_ctrl_inv_t = libRigging.create_utility_node('multiplyDivide', input1=self.node.t, input2=[-1, -1, -1]).output attr_ctrl_inv_r = libRigging.create_utility_node('multiplyDivide', input1=self.node.r, input2=[-1, -1, -1]).output attr_ctrl_inv_t = libRigging.create_utility_node( 'multiplyDivide', input1=attr_ctrl_inv_t, input2X=self.attr_sensitivity_tx, input2Y=self.attr_sensitivity_ty, input2Z=self.attr_sensitivity_tz).output if flip_lr: attr_doritos_tx = libRigging.create_utility_node( 'multiplyDivide', input1X=attr_ctrl_inv_t.outputX, input2X=-1).outputX else: attr_doritos_tx = attr_ctrl_inv_t.outputX attr_doritos_ty = attr_ctrl_inv_t.outputY attr_doritos_tz = attr_ctrl_inv_t.outputZ pymel.connectAttr(attr_doritos_tx, layer_doritos.tx) pymel.connectAttr(attr_doritos_ty, layer_doritos.ty) pymel.connectAttr(attr_doritos_tz, layer_doritos.tz) pymel.connectAttr(attr_ctrl_inv_r, layer_doritos.r) # Apply scaling on the ctrl parent. # This is were the 'black magic' happen. if flip_lr: attr_ctrl_offset_sx_inn = libRigging.create_utility_node( 'multiplyDivide', input1X=self.attr_sensitivity_tx, input2X=-1).outputX else: attr_ctrl_offset_sx_inn = self.attr_sensitivity_tx attr_ctrl_offset_sy_inn = self.attr_sensitivity_ty attr_ctrl_offset_sz_inn = self.attr_sensitivity_tz pymel.connectAttr(attr_ctrl_offset_sx_inn, self.offset.scaleX) pymel.connectAttr(attr_ctrl_offset_sy_inn, self.offset.scaleY) pymel.connectAttr(attr_ctrl_offset_sz_inn, self.offset.scaleZ) # Apply sensibility on the ctrl shape ctrl_shape = self.node.getShape() tmp = pymel.duplicate(self.node.getShape())[0] ctrl_shape_orig = tmp.getShape() ctrl_shape_orig.setParent(self.node, relative=True, shape=True) ctrl_shape_orig.rename('{0}Orig'.format(ctrl_shape.name())) pymel.delete(tmp) ctrl_shape_orig.intermediateObject.set(True) for cp in ctrl_shape.cp: cp.set(0, 0, 0) # Counter-scale the shape attr_adjustement_sx_inn = attr_sensibility_lr_inv attr_adjustement_sy_inn = attr_sensibility_ud_inv attr_adjustement_sz_inn = attr_sensibility_fb_inv attr_adjustement_scale = libRigging.create_utility_node( 'composeMatrix', inputScaleX=attr_adjustement_sx_inn, inputScaleY=attr_adjustement_sy_inn, inputScaleZ=attr_adjustement_sz_inn).outputMatrix attr_adjustement_rot = libRigging.create_utility_node( 'composeMatrix', inputRotateX=self.node.rotateX, inputRotateY=self.node.rotateY, inputRotateZ=self.node.rotateZ).outputMatrix attr_adjustement_rot_inv = libRigging.create_utility_node( 'inverseMatrix', inputMatrix=attr_adjustement_rot).outputMatrix attr_adjustement_tm = libRigging.create_utility_node( 'multMatrix', matrixIn=[ attr_adjustement_rot, attr_adjustement_scale, attr_adjustement_rot_inv ]).matrixSum attr_transform_geometry = libRigging.create_utility_node( 'transformGeometry', transform=attr_adjustement_tm, inputGeometry=ctrl_shape_orig.local).outputGeometry pymel.connectAttr(attr_transform_geometry, ctrl_shape.create, force=True) # Constraint ctrl pymel.parentConstraint(layer_doritos, self.offset, maintainOffset=False, skipRotate=['x', 'y', 'z']) pymel.orientConstraint(layer_doritos.getParent(), self.offset, maintainOffset=True) # Clean dag junk if grp_rig: stack.setParent(grp_rig) if fol_mesh: fol_mesh.setParent(grp_rig)
def pre_build(self, create_grp_anm=True, create_display_layers=True, **kwargs): super(RigSqueeze, self).pre_build(create_grp_anm=create_grp_anm, create_master_grp=False, create_display_layers=create_display_layers, **kwargs) if create_grp_anm: grp_anim_size = CtrlMaster._get_recommended_radius(self) self.grp_anm_master = self.build_grp( CtrlMaster, self.grp_anm_master, self.nomenclature.root_anm_master_name, size=grp_anim_size ) if create_display_layers: pymel.editDisplayLayerMembers(self.layer_anm, self.grp_anm_master, noRecurse=True) # # Create specific group related to squeeze rig convention # all_geos = libPymel.ls_root_geos() # Build All_Grp self.grp_master = self.build_grp(classRig.RigGrp, self.grp_master, self.nomenclature.root_all_name) self.grp_model = self.build_grp(classRig.RigGrp, self.grp_model, self.nomenclature.root_model_name) self.grp_proxy = self.build_grp(classRig.RigGrp, self.grp_proxy, self.nomenclature.root_proxy_name) self.grp_fx = self.build_grp(classRig.RigGrp, self.grp_fx, self.nomenclature.root_fx_name) # Parent all groups in the main grp_master pymel.parent(self.grp_anm_master, self.grp_master) pymel.parent(self.grp_anm, self.grp_anm_master) # grp_anm is not a Node, but a Ctrl self.grp_rig.setParent(self.grp_master) self.grp_fx.setParent(self.grp_master) self.grp_model.setParent(self.grp_master) self.grp_proxy.setParent(self.grp_master) self.grp_geo.setParent(self.grp_master) ''' if self.grp_jnt.getParent() is None: self.grp_jnt.setParent(self.grp_master) ''' # Lock and hide all attributes we don't want the animator to play with libAttr.lock_hide_trs(self.grp_master) libAttr.lock_hide_trs(self.grp_rig) libAttr.lock_hide_trs(self.grp_fx) libAttr.lock_hide_trs(self.grp_model) libAttr.lock_hide_trs(self.grp_proxy) libAttr.lock_hide_trs(self.grp_geo) libAttr.hide_scale(self.grp_anm) # Hide some group # self.grp_jnt.visibility.set(False) self.grp_rig.visibility.set(False) self.grp_fx.visibility.set(False) self.grp_model.visibility.set(False) # # Add root ctrl attributes specific to squeeze while preserving existing connections. # if not self.grp_anm.hasAttr(self.GROUP_NAME_DISPLAY, checkShape=False): libAttr.addAttr_separator(self.grp_anm, self.GROUP_NAME_DISPLAY) attr_display_mesh_output_attrs = {self.grp_geo.visibility} attr_display_proxy_output_attrs = {self.grp_proxy.visibility} # attr_display_ctrl_output_attrs = set( # [children.visibility for children in self.grp_anm.getChildren(type='transform')] # ) # In the past, the displayMesh attribute was a boolean and the displayProxy was also a boolean. # Now we use an enum. This mean that we need to remap. if self.grp_anm.hasAttr(self.ATTR_NAME_DISPLAY_MESH): attr_display_mesh = self.grp_anm.attr(self.ATTR_NAME_DISPLAY_MESH) if attr_display_mesh.type() == 'short': for attr_dst in attr_display_mesh.outputs(plugs=True): attr_display_mesh_output_attrs.add(attr_dst) pymel.disconnectAttr(attr_display_mesh, attr_dst) if self.grp_anm.hasAttr(self.ATTR_NAME_DISPLAY_PROXY): attr_display_proxy = self.grp_anm.attr(self.ATTR_NAME_DISPLAY_PROXY) for attr_dst in attr_display_proxy.outputs(plugs=True): attr_display_proxy_output_attrs.add(attr_dst) pymel.disconnectAttr(attr_display_proxy, attr_dst) attr_display_proxy.delete() # Create DisplayMesh attribute attr_display_mesh = self._init_attr_display_mesh() # Create DisplayCtrl attribute attr_display_ctrl = self._init_attr_display_ctrl() # Connect DisplayMesh attribute for attr_dst in attr_display_mesh_output_attrs: if not libAttr.is_connected_to(attr_display_mesh, attr_dst, max_depth=3): self.debug("Connecting {} to {}".format(attr_display_mesh, attr_dst)) attr_proxy_display_inn = libRigging.create_utility_node( 'condition', firstTerm=attr_display_mesh, secondTerm=0, colorIfTrueR=True, colorIfFalseR=False ).outColorR pymel.connectAttr(attr_proxy_display_inn, attr_dst, force=True) for attr_dst in attr_display_proxy_output_attrs: if not libAttr.is_connected_to(attr_display_mesh, attr_dst, max_depth=3): self.debug("Connecting {} to {}".format(attr_display_mesh, attr_dst)) # attr_proxy_display_inn = libRigging.create_utility_node( # 'condition', # firstTerm=attr_display_mesh, # secondTerm=0, # colorIfTrueR=True, # colorIfFalseR=False # ).outColorR pymel.connectAttr(attr_display_mesh, attr_dst, force=True)
def build(self, constraint=True, constraint_handle=True, setup_softik=True, **kwargs): """ :param constraint: Bool to tell if we will constraint the chain bone on the ikchain :param constraint_handle: Bool to tell if we will contraint the handle on the ik ctrl :param setup_softik: Bool to tell if we setup the soft ik system :param kwargs: More kwargs passed to the superclass :return: Nothing """ # Build the softik node after the setup for the quadruped super(LegIkQuad, self).build(constraint=False, constraint_handle=False, setup_softik=False, **kwargs) nomenclature_rig = self.get_nomenclature_rig() quad_swivel_pos = self.calc_swivel_pos(start_index=1, end_index=3) heel_idx = self.iCtrlIndex - 1 # Hack: Re-parent ehe ik chain as a workaround for the spring ik solver bug. # Otherwise the current parent (which is constrained) will trigger and old sprint ik solver bug # that will result in double rotation. # src: http://forums.cgsociety.org/showthread.php?t=936724 ik_chain_start = self._chain_ik[0] ik_chain_start.setParent(self.grp_rig) pymel.parentConstraint(self._ikChainGrp, ik_chain_start, maintainOffset=True, skipRotate=['x', 'y', 'z']) # Create a second ik chain for the quadruped setup self._chain_quad_ik = self.chain.duplicate() for i, oIk in enumerate(self._chain_quad_ik): oIk.rename(nomenclature_rig.resolve('QuadChain{0:02}'.format(i))) # Constraint the bones after the iCtrlIdx to the first ik chain to make the foot roll work correctly if i > self.iCtrlIndex: pymel.parentConstraint(self._chain_ik[i], self._chain_quad_ik[i]) self._chain_quad_ik[0].setParent(self._chain_ik[0]) # Hack: Since we are using direct connection on the first joint of the quad_ik chain, # there might be situation where Maya will give an initial rotation of (180, 180, 180) instead of (0, 0, 0). # To prevent this we'll manually make sure that the rotation is zeroed out. self._chain_quad_ik[0].r.set(0, 0, 0) obj_e_ik = self._chain_ik[self.iCtrlIndex] obj_e_quadik = self._chain_quad_ik[self.iCtrlIndex] # We need a second ik solver for the quad chain ik_solver_quad_name = nomenclature_rig.resolve('quadIkHandle') ik_effector_quad_name = nomenclature_rig.resolve('quadIkEffector') self._ik_handle_quad, _ik_effector = pymel.ikHandle(startJoint=self._chain_quad_ik[1], endEffector=obj_e_quadik, solver='ikRPsolver') self._ik_handle_quad.rename(ik_solver_quad_name) _ik_effector.rename(ik_effector_quad_name) self._ik_handle_quad.setParent(self._ik_handle) # # Create softIk node and connect user accessible attributes to it. # if setup_softik: self.setup_softik([self._ik_handle, self._ik_handle_quad], [self._chain_ik, self._chain_quad_ik]) # Create another swivel handle node for the quad chain setup self.ctrl_swivel_quad = self.setup_swivel_ctrl(self.ctrl_swivel_quad, self._chain_quad_ik[heel_idx], quad_swivel_pos, self._ik_handle_quad, name='swivelQuad', mirror_setup=False, adjust_ik_handle_twist=False) # self.quad_swivel_distance = self.chain_length # Used in ik/fk switch # Set by default the space to calf if self.ctrl_swivel_quad.space: enum = self.ctrl_swivel_quad.space.getEnums() calf_idx = enum.get('Calf', None) if calf_idx: self.ctrl_swivel_quad.space.set(calf_idx) # Expose 'pitch' Quadruped-specific attribute. attr_holder = self.ctrl_ik libAttr.addAttr_separator(attr_holder, 'Quadruped', niceName='Quadruped') attr_pitch = libAttr.addAttr(attr_holder, longName='pitch', k=True) pymel.connectAttr(attr_pitch, self._chain_quad_ik[0].rotateZ) pymel.orientConstraint(obj_e_ik, obj_e_quadik, maintainOffset=True) if constraint: for source, target in zip(self._chain_quad_ik, self.chain): # Note that maintainOffset should not be necessary, however in some rare case even after all the # adjustments we do, the rotation of the influence might be flipped for no particular reasons. # (see Task #70938). pymel.parentConstraint(source, target, maintainOffset=True)
def build(self, attr_holder=None, constraint_handle=False, setup_softik=True, **kwargs): """ Build the LegIk system :param attr_holder: The attribute holder object for all the footroll params :param kwargs: More kwargs pass to the superclass :return: Nothing """ # Compute ctrl_ik orientation # Hack: Bypass pymel bug (see https://github.com/LumaPictures/pymel/issues/355) ctrl_ik_orientation = pymel.datatypes.TransformationMatrix(self._get_reference_plane()).rotate super(LegIk, self).build(ctrl_ik_orientation=ctrl_ik_orientation, constraint_handle=constraint_handle, setup_softik=setup_softik, **kwargs) nomenclature_rig = self.get_nomenclature_rig() jnts = self._chain_ik[self.iCtrlIndex:] num_jnts = len(jnts) if num_jnts == 4: jnt_foot, jnt_heel, jnt_toes, jnt_tip = jnts elif num_jnts == 3: jnt_foot, jnt_toes, jnt_tip = jnts jnt_heel = None else: raise Exception("Unexpected number of joints after the limb. Expected 3 or 4, got {0}".format(num_jnts)) # Create FootRoll (chain?) pos_foot = pymel.datatypes.Point(jnt_foot.getTranslation(space='world')) pos_heel = pymel.datatypes.Point(jnt_heel.getTranslation(space='world')) if jnt_heel else None pos_toes = pymel.datatypes.Point(jnt_toes.getTranslation(space='world')) pos_tip = pymel.datatypes.Point(jnt_tip.getTranslation(space='world')) # Resolve pivot locations tm_ref = self._get_reference_plane() tm_ref_dir = pymel.datatypes.Matrix( # Used to compute raycast directions tm_ref.a00, tm_ref.a01, tm_ref.a02, tm_ref.a03, tm_ref.a10, tm_ref.a11, tm_ref.a12, tm_ref.a13, tm_ref.a20, tm_ref.a21, tm_ref.a22, tm_ref.a23, 0, 0, 0, 1 ) # # Resolve pivot positions # geometries = self.rig.get_meshes() # Resolve pivot inn if self.pivot_foot_inn_pos: pos_pivot_inn = pymel.datatypes.Point(self.pivot_foot_inn_pos) * tm_ref else: pos_pivot_inn = self._get_recommended_pivot_bank(geometries, tm_ref, tm_ref_dir, pos_toes, direction=-1) # Resolve pivot bank out if self.pivot_foot_out_pos: pos_pivot_out = pymel.datatypes.Point(self.pivot_foot_out_pos) * tm_ref else: pos_pivot_out = self._get_recommended_pivot_bank(geometries, tm_ref, tm_ref_dir, pos_toes, direction=1) # Resolve pivot Back if self.pivot_foot_back_pos: pos_pivot_back = pymel.datatypes.Point(self.pivot_foot_back_pos) * tm_ref else: pos_pivot_back = self._get_recommended_pivot_back(geometries, tm_ref, tm_ref_dir, pos_toes) # Set pivot Front if self.pivot_foot_front_pos: pos_pivot_front = pymel.datatypes.Point(self.pivot_foot_front_pos) * tm_ref else: pos_pivot_front = self._get_recommended_pivot_front(geometries, tm_ref, tm_ref_dir, pos_toes, pos_tip) # Set pivot Ankle if self.pivot_toes_ankle_pos: pos_pivot_ankle = pymel.datatypes.Point(self.pivot_toes_ankle_pos) * tm_ref else: pos_pivot_ankle = pos_toes # Set pivot Heel floor if self.pivot_toes_heel_pos: pos_pivot_heel = pymel.datatypes.Point(self.pivot_toes_heel_pos) * tm_ref else: if jnt_heel: pos_pivot_heel = pos_heel else: pos_pivot_heel = pymel.datatypes.Point(pos_foot) pos_pivot_heel.y = 0 # # Build Setup # root_footRoll = pymel.createNode('transform', name=nomenclature_rig.resolve('footRoll')) # Align all pivots to the reference plane root_footRoll.setMatrix(tm_ref) # Create pivots hierarchy self.pivot_toes_heel = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotToesHeel')) self.pivot_toes_ankle = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotToesAnkle')) self.pivot_foot_ankle = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotFootAnkle')) self.pivot_foot_front = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotFootFront')) self.pivot_foot_back = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotFootBack')) self.pivot_foot_inn = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotFootBankInn')) self.pivot_foot_out = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotFootBankOut')) self.pivot_foot_heel = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotFootHeel')) self.pivot_foot_toes_fk = pymel.spaceLocator(name=nomenclature_rig.resolve('pivotToesFkRoll')) chain_footroll = [ root_footRoll, self.pivot_foot_ankle, self.pivot_foot_inn, self.pivot_foot_out, self.pivot_foot_back, self.pivot_foot_heel, self.pivot_foot_front, self.pivot_toes_ankle, self.pivot_toes_heel ] libRigging.create_hyerarchy(chain_footroll) chain_footroll[0].setParent(self.grp_rig) self.pivot_foot_toes_fk.setParent(self.pivot_foot_heel) self.pivot_foot_ankle.setTranslation(pos_pivot_ankle, space='world') self.pivot_foot_inn.setTranslation(pos_pivot_inn, space='world') self.pivot_foot_out.setTranslation(pos_pivot_out, space='world') self.pivot_foot_back.setTranslation(pos_pivot_back, space='world') self.pivot_foot_heel.setTranslation(pos_pivot_heel, space='world') self.pivot_foot_front.setTranslation(pos_pivot_front, space='world') self.pivot_toes_ankle.setTranslation(pos_pivot_ankle, space='world') self.pivot_foot_toes_fk.setTranslation(pos_pivot_ankle, space='world') self.pivot_toes_heel.setTranslation(pos_pivot_heel, space='world') # Create attributes attr_holder = self.ctrl_ik libAttr.addAttr_separator(attr_holder, 'footRoll', niceName='Foot Roll') attr_inn_roll_auto = libAttr.addAttr(attr_holder, longName='rollAuto', k=True) attr_inn_roll_auto_threshold = libAttr.addAttr(attr_holder, longName='rollAutoThreshold', k=True, defaultValue=25) attr_inn_bank = libAttr.addAttr(attr_holder, longName='bank', k=True) attr_inn_ankle_rotz = libAttr.addAttr(attr_holder, longName=self.ANKLE_ROTZ_LONGNAME, niceName=self.ANKLE_ROTZ_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=-90, maxValue=90) attr_inn_back_rotx = libAttr.addAttr(attr_holder, longName=self.BACK_ROTX_LONGNAME, niceName=self.BACK_ROTX_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=-90, maxValue=0) attr_inn_ankle_rotx = libAttr.addAttr(attr_holder, longName=self.ANKLE_ROTX_LONGNAME, niceName=self.ANKLE_ROTX_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=90) attr_inn_front_rotx = libAttr.addAttr(attr_holder, longName=self.FRONT_ROTX_LONGNAME, niceName=self.FRONT_ROTX_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=90) attr_inn_back_roty = libAttr.addAttr(attr_holder, longName=self.BACK_ROTY_LONGNAME, niceName=self.BACK_ROTY_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=-90, maxValue=90) attr_inn_heel_roty = libAttr.addAttr(attr_holder, longName=self.HEEL_ROTY_LONGNAME, niceName=self.HEEL_ROTY_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=-90, maxValue=90) attr_inn_toes_roty = libAttr.addAttr(attr_holder, longName=self.TOES_ROTY_LONGNAME, niceName=self.TOES_ROTY_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=-90, maxValue=90) attr_inn_front_roty = libAttr.addAttr(attr_holder, longName=self.FRONT_ROTY_LONGNAME, niceName=self.FRONT_ROTY_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=-90, maxValue=90) attr_inn_toes_fk_rotx = libAttr.addAttr(attr_holder, longName=self.TOESFK_ROTX_LONGNAME, niceName=self.TOESFK_ROTX_NICENAME, k=True, hasMinValue=True, hasMaxValue=True, minValue=-90, maxValue=90) attr_roll_auto_pos = libRigging.create_utility_node('condition', operation=2, firstTerm=attr_inn_roll_auto, secondTerm=0, colorIfTrueR=attr_inn_roll_auto, colorIfFalseR=0.0).outColorR # Greater attr_roll_auto_f = libRigging.create_utility_node('condition', operation=2, firstTerm=attr_inn_roll_auto, secondTerm=attr_inn_roll_auto_threshold, colorIfFalseR=0, colorIfTrueR=( libRigging.create_utility_node('plusMinusAverage', operation=2, input1D=[ attr_inn_roll_auto, attr_inn_roll_auto_threshold]).output1D) ).outColorR # Substract attr_roll_auto_b = libRigging.create_utility_node('condition', operation=2, firstTerm=attr_inn_roll_auto, secondTerm=0.0, colorIfTrueR=0, colorIfFalseR=attr_inn_roll_auto ).outColorR # Greater attr_roll_m = libRigging.create_utility_node('addDoubleLinear', input1=attr_roll_auto_pos, input2=attr_inn_ankle_rotx).output attr_roll_f = libRigging.create_utility_node('addDoubleLinear', input1=attr_roll_auto_f, input2=attr_inn_front_rotx).output attr_roll_b = libRigging.create_utility_node('addDoubleLinear', input1=attr_roll_auto_b, input2=attr_inn_back_rotx).output attr_bank_inn = libRigging.create_utility_node('condition', operation=2, firstTerm=attr_inn_bank, secondTerm=0, colorIfTrueR=attr_inn_bank, colorIfFalseR=0.0 ).outColorR # Greater attr_bank_out = libRigging.create_utility_node('condition', operation=4, firstTerm=attr_inn_bank, secondTerm=0, colorIfTrueR=attr_inn_bank, colorIfFalseR=0.0).outColorR # Less pymel.connectAttr(attr_roll_m, self.pivot_toes_ankle.rotateX) pymel.connectAttr(attr_roll_f, self.pivot_foot_front.rotateX) pymel.connectAttr(attr_roll_b, self.pivot_foot_back.rotateX) pymel.connectAttr(attr_bank_inn, self.pivot_foot_inn.rotateZ) pymel.connectAttr(attr_bank_out, self.pivot_foot_out.rotateZ) pymel.connectAttr(attr_inn_heel_roty, self.pivot_foot_heel.rotateY) pymel.connectAttr(attr_inn_front_roty, self.pivot_foot_front.rotateY) pymel.connectAttr(attr_inn_back_roty, self.pivot_foot_back.rotateY) pymel.connectAttr(attr_inn_ankle_rotz, self.pivot_toes_heel.rotateZ) pymel.connectAttr(attr_inn_toes_roty, self.pivot_foot_ankle.rotateY) pymel.connectAttr(attr_inn_toes_fk_rotx, self.pivot_foot_toes_fk.rotateX) # Create ikHandles and parent them # Note that we are directly parenting them so the 'Preserve Child Transform' of the translate tool still work. if jnt_heel: ikHandle_foot, ikEffector_foot = pymel.ikHandle(startJoint=jnt_foot, endEffector=jnt_heel, solver='ikSCsolver') else: ikHandle_foot, ikEffector_foot = pymel.ikHandle(startJoint=jnt_foot, endEffector=jnt_toes, solver='ikSCsolver') ikHandle_foot.rename(nomenclature_rig.resolve('ikHandle', 'foot')) ikHandle_foot.setParent(self.grp_rig) ikHandle_foot.setParent(self.pivot_toes_heel) if jnt_heel: ikHandle_heel, ikEffector_foot = pymel.ikHandle(startJoint=jnt_heel, endEffector=jnt_toes, solver='ikSCsolver') ikHandle_heel.rename(nomenclature_rig.resolve('ikHandle', 'heel')) ikHandle_heel.setParent(self.grp_rig) ikHandle_heel.setParent(self.pivot_foot_front) ikHandle_toes, ikEffector_toes = pymel.ikHandle(startJoint=jnt_toes, endEffector=jnt_tip, solver='ikSCsolver') ikHandle_toes.rename(nomenclature_rig.resolve('ikHandle', 'toes')) ikHandle_toes.setParent(self.grp_rig) ikHandle_toes.setParent(self.pivot_foot_toes_fk) # Hack: Re-constraint foot ikhandle # todo: cleaner! pymel.parentConstraint(self.ctrl_ik, root_footRoll, maintainOffset=True) # Connect the footroll to the main ikHandle # Note that we also need to hijack the softik network. fn_can_delete = lambda x: isinstance(x, pymel.nodetypes.Constraint) and \ not isinstance(x, pymel.nodetypes.PoleVectorConstraint) pymel.delete(filter(fn_can_delete, self._ik_handle_target.getChildren())) if jnt_heel: pymel.parentConstraint(self.pivot_toes_heel, self._ik_handle_target, maintainOffset=True) else: pymel.parentConstraint(self.pivot_toes_ankle, self._ik_handle_target, maintainOffset=True) ''' # Constraint swivel to ctrl_ik pymel.parentConstraint(self.ctrl_ik, self.ctrl_swivel, maintainOffset=True) # TODO: Implement SpaceSwitch ''' # Handle globalScale pymel.connectAttr(self.grp_rig.globalScale, root_footRoll.scaleX) pymel.connectAttr(self.grp_rig.globalScale, root_footRoll.scaleY) pymel.connectAttr(self.grp_rig.globalScale, root_footRoll.scaleZ)
def build_stack(self, stack, **kwargs): nomenclature_rig = self.get_nomenclature_rig() jnt_head = self.rig.get_head_jnt() jnt_jaw = self.rig.get_jaw_jnt() # # Create additional attributes to control the jaw layer # libAttr.addAttr_separator(self.grp_rig, 'jawLayer') self._attr_inn_jaw_ratio_default = libAttr.addAttr( self.grp_rig, 'jawRatioDefault', defaultValue=0.5, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, k=True ) self._attr_bypass_splitter = libAttr.addAttr( self.grp_rig, 'jawSplitterBypass', defaultValue=0.0, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, k=True ) # # Create reference objects used for calculations. # # Create a reference node that follow the head self._target_head = pymel.createNode( 'transform', name=nomenclature_rig.resolve('innHead'), parent=self.grp_rig ) self._target_head.setTranslation(jnt_head.getTranslation(space='world')) pymel.parentConstraint(jnt_head, self._target_head, maintainOffset=True) pymel.scaleConstraint(jnt_head, self._target_head, maintainOffset=True) # Create a reference node that follow the jaw initial position jaw_pos = jnt_jaw.getTranslation(space='world') self._target_jaw_bindpose = pymel.createNode( 'transform', name=nomenclature_rig.resolve('innJawBindPose'), parent=self.grp_rig ) self._target_jaw_bindpose.setTranslation(jaw_pos) # Create a reference node that follow the jaw self._target_jaw = pymel.createNode( 'transform', name=nomenclature_rig.resolve('innJaw'), parent=self._target_jaw_bindpose ) self._target_jaw.t.set(0,0,0) pymel.parentConstraint(jnt_jaw, self._target_jaw, maintainOffset=True) pymel.scaleConstraint(jnt_jaw, self._target_jaw, maintainOffset=True) # Create a node that contain the out jaw influence. # Note that the out jaw influence can be modified by the splitter node. grp_parent_pos = self._grp_parent.getTranslation(space='world') # grp_offset is always in world coordinates self._jaw_ref = pymel.createNode( 'transform', name=nomenclature_rig.resolve('outJawInfluence'), parent=self.grp_rig ) self._jaw_ref.t.set(grp_parent_pos) pymel.parentConstraint(self._target_jaw, self._jaw_ref, maintainOffset=True) # Extract jaw influence attr_delta_tm = libRigging.create_utility_node('multMatrix', matrixIn=[ self._jaw_ref.worldMatrix, self._grp_parent.worldInverseMatrix ]).matrixSum util_extract_jaw = libRigging.create_utility_node( 'decomposeMatrix', name=nomenclature_rig.resolve('getJawRotation'), inputMatrix=attr_delta_tm ) super(FaceLipsAvar, self).build_stack(stack, **kwargs) # # Create jaw influence layer # Create a reference object to extract the jaw displacement. # # Add the jaw influence as a new stack layer. layer_jaw_r = stack.prepend_layer(name='jawRotate') layer_jaw_t = stack.prepend_layer(name='jawTranslate') pymel.connectAttr(util_extract_jaw.outputTranslate, layer_jaw_t.t) pymel.connectAttr(util_extract_jaw.outputRotate, layer_jaw_r.r)