def get_head_length(self): jnt_head = self.get_head_jnt() if not jnt_head: self.warning("Can't resolve head length!") ref_tm = jnt_head.getMatrix(worldSpace=True) geometries = libRigging.get_affected_geometries(jnt_head) # Resolve the top of the head location bot = pymel.datatypes.Point(ref_tm.translate) #dir = pymel.datatypes.Point(1,0,0) * ref_tm #dir = dir.normal() # This is strange but not pointing to the world sometime don't work... # TODO: FIX ME dir = pymel.datatypes.Point(0,1,0) top = libRigging.ray_cast_farthest(bot, dir, geometries) if not top: self.warning("Can't resolve head top location using raycasts using {0} {1}!".format( bot, dir )) return None return libPymel.distance_between_vectors(bot, top)
def get_head_length(self): jnt_head = self.get_head_jnt() if not jnt_head: self.warning("Can't resolve head length!") ref_tm = jnt_head.getMatrix(worldSpace=True) geometries = libRigging.get_affected_geometries(jnt_head) # Resolve the top of the head location bot = pymel.datatypes.Point(ref_tm.translate) #dir = pymel.datatypes.Point(1,0,0) * ref_tm #dir = dir.normal() # This is strange but not pointing to the world sometime don't work... # TODO: FIX ME dir = pymel.datatypes.Point(0, 1, 0) top = libRigging.ray_cast_farthest(bot, dir, geometries) if not top: self.warning( "Can't resolve head top location using raycasts using {0} {1}!" .format(bot, dir)) return None return libPymel.distance_between_vectors(bot, top)
def get_face_macro_ctrls_distance_from_head(self, multiplier=1.2, default_distance=20): """ :return: The recommended distance between the head middle and the face macro ctrls. """ jnt_head = self.get_head_jnt() if not jnt_head: log.warning( "Cannot resolve desired macro avars distance from head. Using default ({0})" .format(default_distance)) return default_distance ref_tm = jnt_head.getMatrix(worldSpace=True) geometries = libRigging.get_affected_geometries(jnt_head) # Resolve the top of the head location pos = pymel.datatypes.Point(ref_tm.translate) #dir = pymel.datatypes.Point(1,0,0) * ref_tm #dir = dir.normal() # This is strange but not pointing to the world sometime don't work... # TODO: FIX ME dir = pymel.datatypes.Point(0, 1, 0) top = next(iter(libRigging.ray_cast(pos, dir, geometries)), None) if not top: raise Exception("Can't resolve head top location using raycasts!") # Resolve the middle of the head middle = ((top - pos) * 0.5) + pos # Find the front of the face # For now, one raycase seem fine. #dir = pymel.datatypes.Point(0,-1,0) * ref_tm #dir.normalize() dir = pymel.datatypes.Point(0, 0, 1) front = next(iter(libRigging.ray_cast(middle, dir, geometries)), None) if not front: raise Exception( "Can't resolve head front location using raycasts!") distance = libPymel.distance_between_vectors(middle, front) return distance * multiplier
def get_face_macro_ctrls_distance_from_head(self, multiplier=1.2, default_distance=20): """ :return: The recommended distance between the head middle and the face macro ctrls. """ jnt_head = self.get_head_jnt() if not jnt_head: log.warning("Cannot resolve desired macro avars distance from head. Using default ({0})".format(default_distance)) return default_distance ref_tm = jnt_head.getMatrix(worldSpace=True) geometries = libRigging.get_affected_geometries(jnt_head) # Resolve the top of the head location pos = pymel.datatypes.Point(ref_tm.translate) #dir = pymel.datatypes.Point(1,0,0) * ref_tm #dir = dir.normal() # This is strange but not pointing to the world sometime don't work... # TODO: FIX ME dir = pymel.datatypes.Point(0,1,0) top = next(iter(libRigging.ray_cast(pos, dir, geometries)), None) if not top: raise Exception("Can't resolve head top location using raycasts!") # Resolve the middle of the head middle = ((top-pos) * 0.5) + pos # Find the front of the face # For now, one raycase seem fine. #dir = pymel.datatypes.Point(0,-1,0) * ref_tm #dir.normalize() dir = pymel.datatypes.Point(0,0,1) front = next(iter(libRigging.ray_cast(middle, dir, geometries)), None) if not front: raise Exception("Can't resolve head front location using raycasts!") distance = libPymel.distance_between_vectors(middle, front) return distance * multiplier
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(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)