Exemple #1
0
    def _get_follicle_absolute_uv_attr(self, mult_u=1.0, mult_v=1.0):
        """
        Resolve the absolute parameterU and parameterV that will be sent to the follicles.
        :param mult_u: Custom multiplier
        :param mult_v:
        :return: A tuple containing two pymel.Attribute: the absolute parameterU and relative parameterV.
        """
        # TODO: Move attribute definition outside this function.
        attr_u_inn = libAttr.addAttr(self.grp_rig, longName=self._ATTR_NAME_U)
        attr_v_inn = libAttr.addAttr(self.grp_rig, longName=self._ATTR_NAME_V)

        attr_u_relative, attr_v_relative = self._get_follicle_relative_uv_attr(
            mult_u=mult_u, mult_v=mult_v)

        # Add base parameterU & parameterV
        attr_u_cur = libRigging.create_utility_node(
            'addDoubleLinear',
            input1=self._attr_u_base,
            input2=attr_u_relative).output

        attr_v_cur = libRigging.create_utility_node(
            'addDoubleLinear',
            input1=self._attr_v_base,
            input2=attr_v_relative).output

        # TODO: Move attribute connection outside of this function.
        pymel.connectAttr(attr_u_cur, attr_u_inn)
        pymel.connectAttr(attr_v_cur, attr_v_inn)

        return attr_u_inn, attr_v_inn
Exemple #2
0
    def _get_follicle_absolute_uv_attr(self, mult_u=1.0, mult_v=1.0):
        """
        Resolve the absolute parameterU and parameterV that will be sent to the follicles.
        :param mult_u: Custom multiplier
        :param mult_v:
        :return: A tuple containing two pymel.Attribute: the absolute parameterU and relative parameterV.
        """
        # TODO: Move attribute definition outside this function.
        attr_u_inn = libAttr.addAttr(self.grp_rig, longName=self._ATTR_NAME_U)
        attr_v_inn = libAttr.addAttr(self.grp_rig, longName=self._ATTR_NAME_V)

        attr_u_relative, attr_v_relative = self._get_follicle_relative_uv_attr(mult_u=mult_u, mult_v=mult_v)

        # Add base parameterU & parameterV
        attr_u_cur = libRigging.create_utility_node(
            'addDoubleLinear',
            input1=self._attr_u_base,
            input2=attr_u_relative
        ).output

        attr_v_cur = libRigging.create_utility_node(
            'addDoubleLinear',
            input1=self._attr_v_base,
            input2=attr_v_relative
        ).output

        # TODO: Move attribute connection outside of this function.
        pymel.connectAttr(attr_u_cur, attr_u_inn)
        pymel.connectAttr(attr_v_cur, attr_v_inn)

        return attr_u_inn, attr_v_inn
Exemple #3
0
    def setup_softik(self, ik_handle_to_constraint, stretch_chains):
        """
        Setup the softik system a ik system
        :param ik_handle_to_constraint: A list of ik handles to constraint on the soft-ik network.
        :param stretch_chains: A list of chains to connect the stretch to.
        :return: Nothing
        """
        nomenclature_rig = self.get_nomenclature_rig()

        oAttHolder = self.ctrl_ik
        fnAddAttr = functools.partial(libAttr.addAttr, hasMinValue=True, hasMaxValue=True)
        attInRatio = fnAddAttr(oAttHolder, longName='softIkRatio', niceName='SoftIK', defaultValue=0, minValue=0,
                               maxValue=50, k=True)
        attInStretch = fnAddAttr(oAttHolder, longName='stretch', niceName='Stretch', defaultValue=0, minValue=0,
                                 maxValue=1.0, k=True)
        # Adjust the ratio in percentage so animators understand that 0.03 is 3%
        attInRatio = libRigging.create_utility_node('multiplyDivide', input1X=attInRatio, input2X=0.01).outputX

        # Create and configure SoftIK solver
        soft_ik_network_name = nomenclature_rig.resolve('softik')
        soft_ik_network = SoftIkNode()
        soft_ik_network.build(name=soft_ik_network_name)
        soft_ik_network.setParent(self.grp_rig)

        pymel.connectAttr(attInRatio, soft_ik_network.inRatio)
        pymel.connectAttr(attInStretch, soft_ik_network.inStretch)
        pymel.connectAttr(self._ikChainGrp.worldMatrix, soft_ik_network.inMatrixS)
        pymel.connectAttr(self._ik_handle_target.worldMatrix, soft_ik_network.inMatrixE)
        attr_distance = libFormula.parse('distance*globalScale',
                                         distance=self.chain_length,
                                         globalScale=self.grp_rig.globalScale)
        pymel.connectAttr(attr_distance, soft_ik_network.inChainLength)

        attOutRatio = soft_ik_network.outRatio
        attOutRatioInv = libRigging.create_utility_node('reverse', inputX=soft_ik_network.outRatio).outputX
        # TODO: Improve softik ratio when using multiple ik handle. Not the same ratio will be used depending of the angle
        for handle in ik_handle_to_constraint:
            pointConstraint = pymel.pointConstraint(self._ik_handle_target, self._ikChainGrp, handle)
            pointConstraint.rename(pointConstraint.stripNamespace().replace('pointConstraint', 'softIkConstraint'))
            weight_inn, weight_out = pointConstraint.getWeightAliasList()[-2:]  # Ensure to get the latest target added
            pymel.connectAttr(attOutRatio, weight_inn)
            pymel.connectAttr(attOutRatioInv, weight_out)

        # Connect stretch
        for stretch_chain in stretch_chains:
            for i in range(1, self.iCtrlIndex + 1):
                obj = stretch_chain[i]
                util_get_t = libRigging.create_utility_node(
                    'multiplyDivide',
                    input1X=soft_ik_network.outStretch,
                    input1Y=soft_ik_network.outStretch,
                    input1Z=soft_ik_network.outStretch,
                    input2=obj.t.get()
                )
                pymel.connectAttr(util_get_t.outputX, obj.tx, force=True)
                pymel.connectAttr(util_get_t.outputY, obj.ty, force=True)
                pymel.connectAttr(util_get_t.outputZ, obj.tz, force=True)

        return soft_ik_network
Exemple #4
0
    def build(self, *args, **kwargs):
        super(AdditiveFK, self).build(*args, **kwargs)

        nomenclature_anm = self.get_nomenclature_anm()

        # Ensure to create the finger ctrl in the good orientation
        if nomenclature_anm.side == self.rig.nomenclature.SIDE_L:
            normal_data = {
                constants.Axis.x: (1, 0, 0),
                constants.Axis.y: (0, 1, 0),
                constants.Axis.z: (0, 0, 1)
            }
        else:
            normal_data = {
                constants.Axis.x: (-1, 0, 0),
                constants.Axis.y: (0, -1, 0),
                constants.Axis.z: (0, 0, -1)
            }

        # TODO: Support multiple additive ctrls
        # TODO: Rename
        self.additive_ctrls = filter(None, self.additive_ctrls)
        if not self.additive_ctrls:
            ctrl_add = CtrlFkAdd()
            self.additive_ctrls.append(ctrl_add)
        #HACK - Temp since we don't support multiple ctrl for the moment
        ctrl_add = self.additive_ctrls[0]
        for i, ctrl in enumerate(self.additive_ctrls):
            name = nomenclature_anm.resolve("addFk{0:02d}".format(i))
            ctrl.build(name=name,
                       refs=self.chain.start,
                       normal=normal_data[self.rig._up_axis])
            ctrl.offset.setMatrix(self.chain.start.getMatrix(worldSpace=True))
            ctrl.setParent(self.grp_anm)

        for i, ctrl in enumerate(self.ctrls):
            #HACK Add a new layer if this is the first ctrl to prevent Gimbal lock problems
            if i == 0:
                ctrl.offset = ctrl.append_layer("gimbal")
            attr_rotate_x = libRigging.create_utility_node(
                'addDoubleLinear',
                input1=ctrl.offset.rotateX.get(),
                input2=ctrl_add.rotateX).output
            attr_rotate_y = libRigging.create_utility_node(
                'addDoubleLinear',
                input1=ctrl.offset.rotateY.get(),
                input2=ctrl_add.rotateY).output
            attr_rotate_z = libRigging.create_utility_node(
                'addDoubleLinear',
                input1=ctrl.offset.rotateZ.get(),
                input2=ctrl_add.rotateZ).output
            pymel.connectAttr(attr_rotate_x, ctrl.offset.rotateX)
            pymel.connectAttr(attr_rotate_y, ctrl.offset.rotateY)
            pymel.connectAttr(attr_rotate_z, ctrl.offset.rotateZ)

        # Constraint the fk ctrls in position to the additive fk ctrls
        pymel.pointConstraint(ctrl_add, self.ctrls[0].offset)
    def _build_avars(self, **kwargs):
        nomenclature_rig = self.get_nomenclature_rig()
        jnt_head = self.get_head_jnt()
        jnt_jaw = self.get_jaw_jnt()
        pos_jaw = jnt_jaw.getTranslation(space='world')

        #
        # Build a network that evaluate the jaw transform in relation with it's bind pose.
        # This will be used by avars to computer the 'jawArcT' and 'jawArcR' layer.
        #
        self._ref_jaw_predeform = pymel.createNode(
            'transform',
            name=nomenclature_rig.resolve('refJawPreDeform'),
            parent=self.grp_rig
        )
        self._ref_jaw_predeform.setTranslation(pos_jaw)

        # Create jaw bind-pose reference
        ref_head = pymel.createNode(
            'transform',
            name=nomenclature_rig.resolve('refJawBefore'),
            parent=self.grp_rig
        )
        ref_head.setTranslation(pos_jaw)
        pymel.parentConstraint(jnt_head, ref_head, maintainOffset=True)

        # Create jaw actual pose reference
        ref_jaw = pymel.createNode(
            'transform',
            name=nomenclature_rig.resolve('refJawAfter'),
            parent=self.grp_rig
        )
        ref_jaw.setTranslation(pos_jaw)
        pymel.parentConstraint(jnt_jaw, ref_jaw, maintainOffset=True)

        # Extract the rotation
        # By using matrix we make sure that there's no flipping introduced.
        # This happen in maya when using constraint with multiple targets since
        # Maya use individual values in it's computations.
        attr_get_relative_tm = libRigging.create_utility_node(
            'multMatrix',
            matrixIn=(
                ref_jaw.worldMatrix,
                ref_head.worldInverseMatrix,
            )
        ).matrixSum

        util_get_rotation_euler = libRigging.create_utility_node(
            'decomposeMatrix',
            inputMatrix=attr_get_relative_tm
        )

        self._attr_jaw_pt = util_get_rotation_euler.outputRotateX
        self._attr_jaw_yw = util_get_rotation_euler.outputRotateY
        self._attr_jaw_rl = util_get_rotation_euler.outputRotateZ

        super(FaceLips, self)._build_avars(**kwargs)
    def connect(self, avar, avar_grp, ud=True, fb=True, lr=True, yw=True, pt=True, rl=True, sx=True, sy=True, sz=True):
        need_flip = avar.need_flip_lr()

        # Position
        if ud:
            attr_inn_ud = self.ctrl.translateY
            libRigging.connectAttr_withBlendWeighted(attr_inn_ud, avar.attr_ud)

        if lr:
            attr_inn_lr = self.ctrl.translateX

            if need_flip:
                attr_inn_lr = libRigging.create_utility_node('multiplyDivide', input1X=attr_inn_lr,
                                                             input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_lr, avar.attr_lr)

        if fb:
            attr_inn_fb = self.ctrl.translateZ
            libRigging.connectAttr_withBlendWeighted(attr_inn_fb, avar.attr_fb)

        # Rotation
        if yw:
            attr_inn_yw = self.ctrl.rotateY

            if need_flip:
                attr_inn_yw = libRigging.create_utility_node('multiplyDivide', input1X=attr_inn_yw,
                                                             input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_yw, avar.attr_yw)

        if pt:
            attr_inn_pt = self.ctrl.rotateX
            libRigging.connectAttr_withBlendWeighted(attr_inn_pt, avar.attr_pt)

        if rl:
            attr_inn_rl = self.ctrl.rotateZ

            if need_flip:
                attr_inn_rl = libRigging.create_utility_node('multiplyDivide', input1X=attr_inn_rl,
                                                             input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_rl, avar.attr_rl)

        # Scale
        if sx:
            attr_inn = self.ctrl.scaleX
            libRigging.connectAttr_withBlendWeighted(attr_inn, avar.attr_sx)
        if sy:
            attr_inn = self.ctrl.scaleY
            libRigging.connectAttr_withBlendWeighted(attr_inn, avar.attr_sy)
        if sz:
            attr_inn = self.ctrl.scaleZ
            libRigging.connectAttr_withBlendWeighted(attr_inn, avar.attr_sz)
Exemple #7
0
    def _connect_ctrl(self,
                      ctrl,
                      attr_ud=None,
                      attr_lr=None,
                      attr_fb=None,
                      attr_yw=None,
                      attr_pt=None,
                      attr_rl=None):
        need_flip = self.need_flip_lr()

        if attr_ud:
            attr_inn_ud = ctrl.translateY
            libRigging.connectAttr_withBlendWeighted(attr_inn_ud, attr_ud)

        if attr_lr:
            attr_inn_lr = ctrl.translateX

            if need_flip:
                attr_inn_lr = libRigging.create_utility_node(
                    'multiplyDivide', input1X=attr_inn_lr, input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_lr, attr_lr)

        if attr_fb:
            attr_inn_fb = ctrl.translateZ
            libRigging.connectAttr_withBlendWeighted(attr_inn_fb, attr_fb)

        if attr_yw:
            attr_inn_yw = ctrl.rotateY

            if need_flip:
                attr_inn_yw = libRigging.create_utility_node(
                    'multiplyDivide', input1X=attr_inn_yw, input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_yw, attr_yw)

        if attr_pt:
            attr_inn_pt = ctrl.rotateX
            libRigging.connectAttr_withBlendWeighted(attr_inn_pt, attr_pt)

        if attr_rl:
            attr_inn_rl = ctrl.rotateZ

            if need_flip:
                attr_inn_rl = libRigging.create_utility_node(
                    'multiplyDivide', input1X=attr_inn_rl, input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_rl, attr_rl)
    def _init_attr_face_module_visiblity_driver(self):
        attr_display_ctrl = self._init_attr_display_ctrl()
        attr_display_mesh = self._init_attr_display_mesh()

        condition_attrs = {
            'operation': 2,  # greater than
            'firstTerm': attr_display_ctrl,
            'secondTerm': attr_display_mesh,
            'colorIfTrueR': True,
            'colorIfFalseR': False
        }

        # Re-use the condition matching the requirements.
        def _filter(obj):
            if not isinstance(obj, pymel.nodetypes.Condition):
                return False
            for attr_name, attr_val in condition_attrs.iteritems():
                attr = obj.attr(attr_name)
                if attr.isDestination():
                    if next(iter(attr.inputs(plugs=True)), None) != attr_val:
                        return False
                else:
                    if attr.get() != attr_val:
                        return False
            return True

        condition = next(iter(node for node in attr_display_ctrl.outputs() if _filter(node)), None)

        if condition is None:
            condition = libRigging.create_utility_node(
                'condition',
                **condition_attrs
            )

        return condition.outColorR
    def _build(self):
        nomenclature_rig = self.get_nomenclature_rig()

        grp_output = pymel.createNode(
            'transform',
            name=nomenclature_rig.resolve('output'),
            parent=self.grp_rig,
        )

        attr_get_t = libRigging.create_utility_node(
            'multiplyDivide',
            input1X=self._attr_inn_lr,
            input1Y=self._attr_inn_ud,
            input1Z=self._attr_inn_fb,
            input2X=self.multiplier_lr,
            input2Y=self.multiplier_ud,
            input2Z=self.multiplier_fb,
        ).output

        pymel.connectAttr(attr_get_t, grp_output.translate)
        pymel.connectAttr(self._attr_inn_pt, grp_output.rotateX)
        pymel.connectAttr(self._attr_inn_yw, grp_output.rotateY)
        pymel.connectAttr(self._attr_inn_rl, grp_output.rotateZ)
        pymel.connectAttr(self._attr_inn_sx, grp_output.scaleX)
        pymel.connectAttr(self._attr_inn_sy, grp_output.scaleY)
        pymel.connectAttr(self._attr_inn_sz, grp_output.scaleZ)

        return grp_output.matrix
Exemple #10
0
    def build(self, *args, **kwargs):
        super(AdditiveFK, self).build(*args, **kwargs)

        nomenclature_anm = self.get_nomenclature_anm()

        # Ensure to create the finger ctrl in the good orientation
        if nomenclature_anm.side == self.rig.nomenclature.SIDE_L:
            normal_data = {constants.Axis.x: (1, 0, 0), constants.Axis.y: (0, 1, 0), constants.Axis.z: (0, 0, 1)}
        else:
            normal_data = {constants.Axis.x: (-1, 0, 0), constants.Axis.y: (0, -1, 0), constants.Axis.z: (0, 0, -1)}

        # TODO: Support multiple additive ctrls
        # TODO: Rename
        self.additive_ctrls = filter(None, self.additive_ctrls)
        if not self.additive_ctrls:
            ctrl_add = CtrlFkAdd()
            self.additive_ctrls.append(ctrl_add)
        #HACK - Temp since we don't support multiple ctrl for the moment
        ctrl_add = self.additive_ctrls[0]
        for i, ctrl in enumerate(self.additive_ctrls):
            name = nomenclature_anm.resolve("addFk{0:02d}".format(i))
            ctrl.build(name=name, refs=self.chain.start, normal=normal_data[self.rig._up_axis])
            ctrl.offset.setMatrix(self.chain.start.getMatrix(worldSpace=True))
            ctrl.setParent(self.grp_anm)

        for i, ctrl in enumerate(self.ctrls):
            #HACK Add a new layer if this is the first ctrl to prevent Gimbal lock problems
            if i == 0:
                ctrl.offset = ctrl.append_layer("gimbal")
            attr_rotate_x = libRigging.create_utility_node('addDoubleLinear',
                                                           input1=ctrl.offset.rotateX.get(),
                                                           input2=ctrl_add.rotateX
                                                           ).output
            attr_rotate_y = libRigging.create_utility_node('addDoubleLinear',
                                                           input1=ctrl.offset.rotateY.get(),
                                                           input2=ctrl_add.rotateY
                                                           ).output
            attr_rotate_z = libRigging.create_utility_node('addDoubleLinear',
                                                           input1=ctrl.offset.rotateZ.get(),
                                                           input2=ctrl_add.rotateZ
                                                           ).output
            pymel.connectAttr(attr_rotate_x, ctrl.offset.rotateX)
            pymel.connectAttr(attr_rotate_y, ctrl.offset.rotateY)
            pymel.connectAttr(attr_rotate_z, ctrl.offset.rotateZ)

        # Constraint the fk ctrls in position to the additive fk ctrls
        pymel.pointConstraint(ctrl_add, self.ctrls[0].offset)
def _connect_matrix_attr_to_transform(attr_tm, node):
    util_decompose = libRigging.create_utility_node(
        'decomposeMatrix',
        inputMatrix=attr_tm
    )
    pymel.connectAttr(util_decompose.outputTranslate, node.translate)
    pymel.connectAttr(util_decompose.outputRotate, node.rotate)
    pymel.connectAttr(util_decompose.outputScale, node.scale)
def _connect_with_blend(attrs, attr_dst, attr_amount):
    """Quick function that create two attributes with a blend factor."""
    attr_blended = libRigging.create_utility_node(
        'blendTwoAttr',
        attributesBlender=attr_amount,
        input=attrs,
    ).output
    pymel.connectAttr(attr_blended, attr_dst)
Exemple #13
0
    def _get_follicle_relative_uv_attr(self, mult_u=1.0, mult_v=1.0):
        """
        Resolve the relative parameterU and parameterV that will be sent to the follicles.
        :return: A tuple containing two pymel.Attribute: the relative parameterU and relative parameterV.
        """
        # Apply custom multiplier
        attr_u = libRigging.create_utility_node(
            'multiplyDivide',
            input1X=self.attr_lr,
            input2X=self._attr_u_mult_inn).outputX

        attr_v = libRigging.create_utility_node(
            'multiplyDivide',
            input1X=self.attr_ud,
            input2X=self._attr_v_mult_inn).outputX

        return attr_u, attr_v
def _connect_with_blend(attr_src, attr_dst, attr_amount):
    """Quick function that create two attributes with a blend factor."""
    attr_blended = libRigging.create_utility_node(
        'multiplyDivide',
        input1X=attr_src,
        input2X=attr_amount,
    ).outputX
    pymel.connectAttr(attr_blended, attr_dst)
Exemple #15
0
    def build_stack(self, stack, aim_target=None, **kwargs):
        nomenclature_rig = self.get_nomenclature_rig()

        # Build an aim node in-place for performance
        # This separated node allow the joints to be driven by the avars.
        aim_grp_name = nomenclature_rig.resolve('lookgrp')
        aim_grp = pymel.createNode('transform', name=aim_grp_name)

        aim_node_name = nomenclature_rig.resolve('looknode')
        aim_node = pymel.createNode('transform', name=aim_node_name)
        aim_node.setParent(aim_grp)

        aim_target_name = nomenclature_rig.resolve('target')
        aim_target = pymel.createNode('transform', name=aim_target_name)


        aim_target.setParent(aim_grp)
        self.target = aim_target

        # Build an upnode for the eyes.
        # I'm not a fan of upnodes but in this case it's better to guessing the joint orient.
        aim_upnode_name = nomenclature_rig.resolve('upnode')

        aim_upnode = pymel.createNode('transform', name=aim_upnode_name)
        #
        aim_upnode.setParent(self.grp_rig)
        pymel.parentConstraint(aim_grp, aim_upnode, maintainOffset=True)


        pymel.aimConstraint(aim_target, aim_node,
                            maintainOffset=True,
                            aimVector=(0.0, 0.0, 1.0),
                            upVector=(0.0, 1.0, 0.0),
                            worldUpObject=aim_upnode,
                            worldUpType='object'
                            )

        # Position objects
        aim_grp.setParent(self._grp_offset)  # todo: add begin , end property
        aim_grp.t.set(0,0,0)
        aim_grp.r.set(0,0,0)
        jnt_tm = self.jnt.getMatrix(worldSpace=True)
        jnt_pos = jnt_tm.translate
        aim_upnode_pos = pymel.datatypes.Point(0,1,0) + jnt_pos
        aim_upnode.setTranslation(aim_upnode_pos, space='world')
        aim_target_pos = pymel.datatypes.Point(0,0,1) + jnt_pos
        aim_target.setTranslation(aim_target_pos, space='world')

        pymel.parentConstraint(aim_node, stack, maintainOffset=True)

        # Convert the rotation to avars to additional values can be added.
        util_decomposeMatrix = libRigging.create_utility_node('decomposeMatrix', inputMatrix=aim_node.matrix)
        libRigging.connectAttr_withBlendWeighted(util_decomposeMatrix.outputTranslateX, self.attr_lr)
        libRigging.connectAttr_withBlendWeighted(util_decomposeMatrix.outputTranslateY, self.attr_ud)
        libRigging.connectAttr_withBlendWeighted(util_decomposeMatrix.outputTranslateZ, self.attr_fb)
        libRigging.connectAttr_withBlendWeighted(util_decomposeMatrix.outputRotateY, self.attr_yw)
        libRigging.connectAttr_withBlendWeighted(util_decomposeMatrix.outputRotateX, self.attr_pt)
        libRigging.connectAttr_withBlendWeighted(util_decomposeMatrix.outputRotateZ, self.attr_rl)
def _connect_matrix_to_trs(attr_inn, obj_out):
    # Connect internal_obj
    decomposeTM = libRigging.create_utility_node(
        'decomposeMatrix',
        inputMatrix=attr_inn
    )
    pymel.connectAttr(decomposeTM.outputTranslate, obj_out.translate)
    pymel.connectAttr(decomposeTM.outputRotate, obj_out.rotate)
    pymel.connectAttr(decomposeTM.outputScale, obj_out.scale)
Exemple #17
0
    def _get_follicle_relative_uv_attr(self, mult_u=1.0, mult_v=1.0):
        """
        Resolve the relative parameterU and parameterV that will be sent to the follicles.
        :return: A tuple containing two pymel.Attribute: the relative parameterU and relative parameterV.
        """
        # Apply custom multiplier
        attr_u = libRigging.create_utility_node(
            'multiplyDivide',
            input1X=self.attr_lr,
            input2X=self._attr_u_mult_inn
        ).outputX

        attr_v = libRigging.create_utility_node(
            'multiplyDivide',
            input1X=self.attr_ud,
            input2X=self._attr_v_mult_inn
        ).outputX

        return attr_u, attr_v
Exemple #18
0
    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)
Exemple #19
0
    def build(self, rig, *args, **kwargs):
        super(AdditiveFK, self).build(rig, *args, **kwargs)

        nomenclature_anm = self.get_nomenclature_anm(rig)

        # TODO: Support multiple additive ctrls
        # TODO: Rename
        self.additive_ctrls = filter(None, self.additive_ctrls)
        if not self.additive_ctrls:
            ctrl_add = CtrlFkAdd()
            self.additive_ctrls.append(ctrl_add)
        #HACK - Temp since we don't support multiple ctrl for the moment
        ctrl_add = self.additive_ctrls[0]
        for i, ctrl in enumerate(self.additive_ctrls):
            name = nomenclature_anm.resolve("addFk{0:02d}".format(i))
            ctrl.build(rig, name=name, refs=self.chain.start)
            ctrl.offset.setMatrix(self.chain.start.getMatrix(worldSpace=True))
            ctrl.setParent(self.grp_anm)

        for i, ctrl in enumerate(self.ctrls):
            #HACK Add a new layer if this is the first ctrl to prevent Gimbal lock problems
            if i == 0:
                ctrl.offset = ctrl.add_layer("gimbal")
            attr_rotate_x = libRigging.create_utility_node('addDoubleLinear',
                                                           input1=ctrl.offset.rotateX.get(),
                                                           input2=ctrl_add.rotateX
                                                           ).output
            attr_rotate_y = libRigging.create_utility_node('addDoubleLinear',
                                                           input1=ctrl.offset.rotateY.get(),
                                                           input2=ctrl_add.rotateY
                                                           ).output
            attr_rotate_z = libRigging.create_utility_node('addDoubleLinear',
                                                           input1=ctrl.offset.rotateZ.get(),
                                                           input2=ctrl_add.rotateZ
                                                           ).output
            pymel.connectAttr(attr_rotate_x, ctrl.offset.rotateX)
            pymel.connectAttr(attr_rotate_y, ctrl.offset.rotateY)
            pymel.connectAttr(attr_rotate_z, ctrl.offset.rotateZ)

        # Constraint the fk ctrls in position to the additive fk ctrls
        pymel.pointConstraint(ctrl_add, self.ctrls[0].offset)
Exemple #20
0
    def follicle_conditon_act(follicle, ctrl_master):
        follicle_condition_node = create_utility_node(
            "condition",
            name="follicle_condition",
            secondTerm=1,
            firstTerm=ctrl_master.springActivation
        )  # firstTerm=ctrl_master.springActivation

        follicle_condition_node.colorIfFalseR.set(0)
        follicle_condition_node.colorIfTrueR.set(2)
        follicle_condition_node.outColorR.connect(
            follicle.simulationMethod)  # cannot connect via utility node.
Exemple #21
0
    def build(self, *args, **kwargs):
        super(AvarJaw, self).build(*args, **kwargs)

        # HACK: Hijack the jaw PT avar so the jaw don't go over 0.
        # TODO: Bulletproof
        attr_pt_out = next(iter(self.attr_pt.outputs(plugs=True, skipConversionNodes=True)), None)

        attr_pt_clamp = libRigging.create_utility_node('condition', operation=2,  # Greater than
                                       firstTerm=self.attr_pt,
                                       colorIfTrueR=self.attr_pt,
                                       colorIfFalseR=0.0
                                       ).outColorR
        pymel.connectAttr(attr_pt_clamp, attr_pt_out, force=True)
def _blend_inn_matrix_attribute(attr_tm, attr_blend_tx, attr_blend_ty, attr_blend_tz, attr_blend_rx,
                                attr_blend_ry, attr_blend_rz, attr_blend_sx, attr_blend_sy, attr_blend_sz):
    # todo: replace with a matrixBlend node?
    u_decompose_a = libRigging.create_utility_node('decomposeMatrix', inputMatrix=attr_tm)

    attr_blend_t = libRigging.create_utility_node(
        'multiplyDivide',
        input1X=u_decompose_a.outputTranslateX,
        input1Y=u_decompose_a.outputTranslateY,
        input1Z=u_decompose_a.outputTranslateZ,
        input2X=attr_blend_tx,
        input2Y=attr_blend_ty,
        input2Z=attr_blend_tz,
    ).output
    attr_blend_r = libRigging.create_utility_node(
        'multiplyDivide',
        input1X=u_decompose_a.outputRotateX,
        input1Y=u_decompose_a.outputRotateY,
        input1Z=u_decompose_a.outputRotateZ,
        input2X=attr_blend_rx,
        input2Y=attr_blend_ry,
        input2Z=attr_blend_rz,
    ).output
    attr_blend_s = libRigging.create_utility_node(
        'multiplyDivide',
        input1X=u_decompose_a.outputScaleX,
        input1Y=u_decompose_a.outputScaleY,
        input1Z=u_decompose_a.outputScaleZ,
        input2X=attr_blend_sx,
        input2Y=attr_blend_sy,
        input2Z=attr_blend_sz,
    ).output

    return libRigging.create_utility_node(
        'composeMatrix',
        inputTranslate=attr_blend_t,
        inputRotate=attr_blend_r,
        inputScale=attr_blend_s,
    ).outputMatrix
Exemple #23
0
    def _connect_ctrl(self, ctrl, attr_ud=None, attr_lr=None, attr_fb=None, attr_yw=None, attr_pt=None, attr_rl=None):
        need_flip = self.need_flip_lr()

        if attr_ud:
            attr_inn_ud = ctrl.translateY
            libRigging.connectAttr_withBlendWeighted(attr_inn_ud, attr_ud)

        if attr_lr:
            attr_inn_lr = ctrl.translateX

            if need_flip:
                attr_inn_lr = libRigging.create_utility_node('multiplyDivide', input1X=attr_inn_lr, input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_lr, attr_lr)

        if attr_fb:
            attr_inn_fb = ctrl.translateZ
            libRigging.connectAttr_withBlendWeighted(attr_inn_fb, attr_fb)

        if attr_yw:
            attr_inn_yw = ctrl.rotateY

            if need_flip:
                attr_inn_yw = libRigging.create_utility_node('multiplyDivide', input1X=attr_inn_yw, input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_yw, attr_yw)

        if attr_pt:
            attr_inn_pt = ctrl.rotateX
            libRigging.connectAttr_withBlendWeighted(attr_inn_pt, attr_pt)

        if attr_rl:
            attr_inn_rl = ctrl.rotateZ

            if need_flip:
                attr_inn_rl = libRigging.create_utility_node('multiplyDivide', input1X=attr_inn_rl, input2X=-1).outputX

            libRigging.connectAttr_withBlendWeighted(attr_inn_rl, attr_rl)
Exemple #24
0
    def build(self, *args, **kwargs):
        super(AvarJaw, self).build(*args, **kwargs)

        # HACK: Hijack the jaw PT avar so the jaw don't go over 0.
        # TODO: Bulletproof
        attr_pt_out = next(
            iter(self.attr_pt.outputs(plugs=True, skipConversionNodes=True)),
            None)

        attr_pt_clamp = libRigging.create_utility_node(
            'condition',
            operation=2,  # Greater than
            firstTerm=self.attr_pt,
            colorIfTrueR=self.attr_pt,
            colorIfFalseR=0.0).outColorR
        pymel.connectAttr(attr_pt_clamp, attr_pt_out, force=True)
Exemple #25
0
    def create_spaceswitch(self, rig, module, parent, add_default=True, default_name=None, add_world=False, **kwargs):
        # TODO: Handle when parent is None?
        nomenclature = rig.nomenclature

        if parent is None:
            log.warning("Can't add space switch on {0}. No parent found!".format(self.node.__melobject__()))
            return

        # Resolve spaceswitch targets
        targets, labels = self.get_spaceswitch_targets(rig, module, parent, add_world=add_world)
        if not targets:
            log.warning("Can't add space switch on {0}. No targets found!".format(self.node.__melobject__()))
            return

        if default_name is None:
            default_name = 'Local'

        # Resolve the niceName of the targets
        for i in range(len(targets)):
            target = targets[i]
            label = labels[i]

            if label is None:
                name = nomenclature(target.name())
                name.remove_extra_tokens()
                labels[i] = name.resolve()

        offset = 0
        if add_default:
            offset += 1
            labels.insert(0, default_name)

        layer_spaceSwitch = self.add_layer('spaceSwitch')
        parent_constraint = pymel.parentConstraint(targets, layer_spaceSwitch, maintainOffset=True, **kwargs)
        attr_space = libAttr.addAttr(self.node, 'space', at='enum', enumName=':'.join(labels), k=True)
        atts_weights = parent_constraint.getWeightAliasList()

        for i, att_weight in enumerate(atts_weights):
            index_to_match = i + offset
            att_enabled = libRigging.create_utility_node(  #Equal
                'condition',
                firstTerm=attr_space,
                secondTerm=index_to_match,
                colorIfTrueR=1,
                colorIfFalseR=0
            ).outColorR
            pymel.connectAttr(att_enabled, att_weight)
Exemple #26
0
    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]
        )
Exemple #27
0
    def create(arg1, arg2):
        arg1, arg2 = OperatorDistance._handle_args(arg1, arg2)
        log.debug('[distance:create] {0} * {1}'.format(arg1, arg2))
        # todo: check if we want to use inMatrix1 & inMatrix2 or point1 & point2
        kwargs = {}

        if isinstance(arg1, pymel.datatypes.Matrix) or (isinstance(arg1, pymel.Attribute) or arg1.type() == 'matrix'):
            kwargs['inMatrix1'] = arg1
        elif isinstance(arg1, pymel.nodetypes.Transform):
            kwargs['inMatrix1'] = arg1.worldMatrix
        else:
            kwargs['point1'] = arg1

        if isinstance(arg2, pymel.datatypes.Matrix) or (isinstance(arg2, pymel.Attribute) or arg2.type() == 'matrix'):
            kwargs['inMatrix2'] = arg2
        elif isinstance(arg2, pymel.nodetypes.Transform):
            kwargs['inMatrix2'] = arg2.worldMatrix
        else:
            kwargs['point2'] = arg2

        return libRigging.create_utility_node('distanceBetween', **kwargs).distance
Exemple #28
0
    def create(arg1, arg2):
        arg1, arg2 = OperatorDistance._handle_args(arg1, arg2)
        log.debug('[distance:create] {0} * {1}'.format(arg1, arg2))
        # todo: check if we want to use inMatrix1 & inMatrix2 or point1 & point2
        kwargs = {}

        if isinstance(arg1, pymel.datatypes.Matrix) or (isinstance(arg1, pymel.Attribute) or arg1.type() == 'matrix'):
            kwargs['inMatrix1'] = arg1
        elif isinstance(arg1, pymel.nodetypes.Transform):
            kwargs['inMatrix1'] = arg1.worldMatrix
        else:
            kwargs['point1'] = arg1

        if isinstance(arg2, pymel.datatypes.Matrix) or (isinstance(arg2, pymel.Attribute) or arg2.type() == 'matrix'):
            kwargs['inMatrix2'] = arg2
        elif isinstance(arg2, pymel.nodetypes.Transform):
            kwargs['inMatrix2'] = arg2.worldMatrix
        else:
            kwargs['point2'] = arg2

        return libRigging.create_utility_node('distanceBetween', **kwargs).distance
Exemple #29
0
def adapt_to_orig_shape(source, shape_orig):
    """
    :param source: source shape to transfer
    :param shape_orig: target to transfer to
     This is based out of Renaud's code on shape to orig when building and unbuilding with omtk to preserve shape info.
    """
    def get_transformGeometry(target):
        return next((hist for hist in target.listHistory()
                     if isinstance(hist, pymel.nodetypes.TransformGeometry)),
                    None)

    # Resolve compensation matrix
    util_transform_geometry = get_transformGeometry(shape_orig)
    if not util_transform_geometry:
        shape_orig.warning(
            "Skipping {}. Cannot find transformGeometry.".format(shape_orig))
        return
    attr_compensation_tm = next(
        iter(util_transform_geometry.transform.inputs(plugs=True)), None)

    if not attr_compensation_tm:
        shape_orig.warning(
            "Skipping {}. Cannot find compensation matrix.".format(shape_orig))
        return

    tmp_transform_geometry = libRigging.create_utility_node(
        'transformGeometry',
        inputGeometry=source.local,
        transform=attr_compensation_tm,
        invertTransform=True)

    # source.getParent().setParent(grp_offset) JG modification source should already be in place
    pymel.connectAttr(tmp_transform_geometry.outputGeometry,
                      shape_orig.create,
                      f=True)

    # Cleanup
    pymel.refresh(force=True)  # but why do I have to refresh?!
    pymel.disconnectAttr(shape_orig.create)
    pymel.delete(tmp_transform_geometry)
Exemple #30
0
    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 adapt_to_orig_shape(source, target):
    """
    :param source: source shape to transfer
    :param target: target to transfer to
     This is based out of Renaud's code on shape to orig when building and unbuilding with omtk to preserve shape info.
    """

    def get_transformGeometry(shape):
        return next((hist for hist in shape.listHistory()
                     if isinstance(hist, pymel.nodetypes.TransformGeometry)), None)

    # Resolve orig shape
    shape_orig = get_orig_shape(target)

    # Resolve compensation matrix
    util_transform_geometry = get_transformGeometry(target)
    if not util_transform_geometry:
        target.warning("Skipping {}. Cannot find transformGeometry.".format(target))
        return
    attr_compensation_tm = next(iter(util_transform_geometry.transform.inputs(plugs=True)), None)

    if not attr_compensation_tm:
        target.warning("Skipping {}. Cannot find compensation matrix.".format(target))
        return

    tmp_transform_geometry = libRigging.create_utility_node(
        'transformGeometry',
        inputGeometry=source.local,
        transform=attr_compensation_tm,
        invertTransform=True
    )

    # source.getParent().setParent(grp_offset) JG modification source should already be in place
    pymel.connectAttr(tmp_transform_geometry.outputGeometry, shape_orig.create)

    # Cleanup
    pymel.refresh(force=True)  # but why do I have to refresh^!
    pymel.disconnectAttr(shape_orig.create)
    pymel.delete(tmp_transform_geometry)
Exemple #32
0
def _localize_matrice_value(world_source,
                            holdMatrix,
                            local_var=None,
                            localize_type=None):
    """
    :param world_source: world source is the sim locator. World_source must be a locator in chain before feeding it to local target.
    :param holdMatrix: bind_pose is is the kept parent of the ctrl data, child to the sim locator
    :param local_var: for "FK" previous sim locator in chain . for "IK" the master grp of the input curve rotation.
    :param localize_type: supported types = "FK","IK"
    :return: decomposeMatrix node
    """

    if localize_type is "IK":
        attr_world = create_utility_node(
            'multMatrix',
            matrixIn=(world_source.worldMatrix, local_var.worldInverseMatrix,
                      holdMatrix.outMatrix)).matrixSum

        decompose_m = create_utility_node('decomposeMatrix',
                                          inputMatrix=attr_world)
        return decompose_m

    elif localize_type is "FK":

        attr_world_bindpose = create_utility_node(
            'multMatrix',
            matrixIn=(holdMatrix.outMatrix, local_var.worldMatrix)).matrixSum

        attr_world_bindpose_inv = create_utility_node(
            'inverseMatrix', inputMatrix=attr_world_bindpose).outputMatrix

        attr_local_tm = create_utility_node(
            'multMatrix',
            matrixIn=(world_source.worldMatrix,
                      attr_world_bindpose_inv)).matrixSum

        decompose_m = create_utility_node('decomposeMatrix',
                                          inputMatrix=attr_local_tm)

    return decompose_m
    def _fix_ctrl_shape(self):
        """
        When the rigger want to resize an InteractiveCtrl, he will modify the ctrl shape 'controlPoints' attributes.
        This can be problematic since the shape 'create' attribute is feed from a transformGeometry node
        to compensate the non-uniform scaling caused by the calibration. This will 'skew' the shape which we don't want.
        We always want to make sure that there's only data in the orig shape 'controlPoints' attributes.
        This method will create a temporary shape that will receive the 'local' attribute from the ctrl shape (which
        contain the deformation from the 'controlPoints' attribute). The 'local' attribute of that shape will then be
        fed back to the orig shape. Finally, all the original 'controlPoints' will be set to zero.
        """
        if self.ctrl is None:
            return
        grp_offset = self.ctrl.offset

        def get_orig_shape(shape):
            return next((hist for hist in shape.listHistory()
                         if isinstance(hist, pymel.nodetypes.NurbsCurve)
                         and hist != shape
                         and hist.intermediateObject.get()), None)

        def get_transformGeometry(shape):
            return next((hist for hist in shape.listHistory()
                         if isinstance(hist, pymel.nodetypes.TransformGeometry)), None)

        for shape in self.ctrl.node.getShapes(noIntermediate=True):
            # Resolve orig shape
            shape_orig = get_orig_shape(shape)
            if not shape_orig:
                self.warning("Skipping {}. Cannot find orig shape.".format(shape))
                continue

            # Resolve compensation matrix
            util_transform_geometry = get_transformGeometry(shape)
            if not util_transform_geometry:
                self.warning("Skipping {}. Cannot find transformGeometry.".format(shape))
                continue
            attr_compensation_tm = next(iter(util_transform_geometry.transform.inputs(plugs=True)), None)
            if not attr_compensation_tm:
                self.warning("Skipping {}. Cannot find compensation matrix.".format(shape))
                continue

            tmp_shape = pymel.createNode('nurbsCurve')
            tmp_shape.getParent().setParent(grp_offset)

            # Apply the inverted compensation matrix to access the desired orig_shape 'create' attr.
            tmp_transform_geometry = libRigging.create_utility_node(
                'transformGeometry',
                inputGeometry=shape.local,
                transform=attr_compensation_tm,
                invertTransform=True
            )
            attr_output_geometry = tmp_transform_geometry.outputGeometry
            pymel.connectAttr(attr_output_geometry, tmp_shape.create)
            pymel.disconnectAttr(tmp_shape.create)

            pymel.connectAttr(tmp_shape.local, shape_orig.create)

            # Remove any extraneous controlPoints coordinates.
            for attr_cp in shape.cp:
                attr_cp.set(0, 0, 0)
            for attr_cp in shape_orig.cp:
                attr_cp.set(0, 0, 0)

            # Cleanup
            pymel.disconnectAttr(shape_orig.create)
            pymel.delete(tmp_shape.getParent())
            pymel.delete(tmp_transform_geometry)
Exemple #34
0
    def _get_follicle_relative_uv_attr(self, **kwargs):
        nomenclature_rig = self.get_nomenclature_rig()

        attr_u, attr_v = super(FaceLipsAvar,
                               self)._get_follicle_relative_uv_attr(**kwargs)

        #
        # Create and connect Splitter Node
        #
        splitter = SplitterNode()
        splitter.build(nomenclature_rig,
                       name=nomenclature_rig.resolve('splitter'))
        splitter.setParent(self.grp_rig)

        # Resolve the radius of the jaw influence. Used by the splitter.
        attr_jaw_radius = libRigging.create_utility_node(
            'distanceBetween',
            name=nomenclature_rig.resolve('getJawRadius'),
            point1=self._grp_offset.translate,
            point2=self._target_jaw_bindpose.translate).distance

        # Resolve the jaw pitch. Used by the splitter.
        attr_jaw_pitch = libRigging.create_utility_node(
            'decomposeMatrix',
            name=nomenclature_rig.resolve('getJawPitch'),
            inputMatrix=libRigging.create_utility_node(
                'multMatrix',
                name=nomenclature_rig.resolve('extractJawPitch'),
                matrixIn=(self._target_jaw.worldMatrix,
                          self._grp_parent.worldInverseMatrix
                          )).matrixSum).outputRotateX

        # Connect the splitter inputs
        pymel.connectAttr(attr_u, splitter.attr_inn_surface_u)
        pymel.connectAttr(attr_v, splitter.attr_inn_surface_v)
        pymel.connectAttr(self._attr_inn_jaw_ratio_default,
                          splitter.attr_inn_jaw_default_ratio)
        pymel.connectAttr(self._attr_length_v,
                          splitter.attr_inn_surface_range_v)
        pymel.connectAttr(attr_jaw_radius, splitter.attr_inn_jaw_radius)
        pymel.connectAttr(attr_jaw_pitch, splitter.attr_inn_jaw_pt)
        pymel.connectAttr(self._attr_bypass_splitter, splitter.attr_inn_bypass)

        attr_u = splitter.attr_out_surface_u
        attr_v = splitter.attr_out_surface_v

        # Create constraint to controller the jaw reference
        attr_jaw_ratio = splitter.attr_out_jaw_ratio
        attr_jaw_ratio_inv = libRigging.create_utility_node(
            'reverse', inputX=attr_jaw_ratio).outputX

        constraint_pr = pymel.parentConstraint(self._target_head,
                                               self._target_jaw,
                                               self._jaw_ref,
                                               maintainOffset=True)
        constraint_s = pymel.scaleConstraint(self._target_head,
                                             self._target_jaw, self._jaw_ref)
        weight_pr_head, weight_pr_jaw = constraint_pr.getWeightAliasList()
        weight_s_head, weight_s_jaw = constraint_s.getWeightAliasList()

        # Connect splitter outputs
        pymel.connectAttr(attr_jaw_ratio_inv, weight_pr_jaw)
        pymel.connectAttr(attr_jaw_ratio_inv, weight_s_jaw)
        pymel.connectAttr(attr_jaw_ratio, weight_pr_head)
        pymel.connectAttr(attr_jaw_ratio, weight_s_head)

        return attr_u, attr_v
    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)
Exemple #36
0
def _make_dynamic_setup(curve,
                        ctrl_master,
                        setup_name="test",
                        rig_high_point=None,
                        nucleus=None,
                        hairSystem=None):
    """creates the default setup for maya dynamics. It affects as little as it can the nucleus because it is re used for all springs
    instead it clamps the value directly in the hairSystem node. It also make the connection to shut the dynamic without
    completly shutting down the follicle. static vs off in the hairSystem node"""
    def follicle_conditon_act(follicle, ctrl_master):
        follicle_condition_node = create_utility_node(
            "condition",
            name="follicle_condition",
            secondTerm=1,
            firstTerm=ctrl_master.springActivation
        )  # firstTerm=ctrl_master.springActivation

        follicle_condition_node.colorIfFalseR.set(0)
        follicle_condition_node.colorIfTrueR.set(2)
        follicle_condition_node.outColorR.connect(
            follicle.simulationMethod)  # cannot connect via utility node.

    time = pm.PyNode("time1")
    if nucleus == None:
        nucleus = create_utility_node(
            "nucleus",
            name="{ctrl_name}_spring_nucleus".format(
                ctrl_name=ctrl_master.name().replace("_Ctrl", "")),
            currentTime=time.outTime,
            startFrame=ctrl_master.startFrame,
            enable=ctrl_master.springActivation,
            subSteps=ctrl_master.springSampling,
            timeScale=ctrl_master.timeScale)
    else:
        if not isinstance(nucleus, pm.nodetypes.Nucleus):
            raise Exception("{} isn\'t a Nucleus type pymel node ".format(
                str(nucleus)))

    follicle = create_utility_node(
        "follicle",
        degree=1,
        startDirection=1,
        restPose=1,
        startPosition=curve.local,
        startPositionMatrix=curve.getParent().worldMatrix[0])

    follicle_p = follicle.getParent()
    pm.rename(follicle_p, "{}_follicle".format(setup_name))
    follicle_p.setParent(rig_high_point)
    pm.makeIdentity(follicle_p, t=True, r=True, s=True)
    pm.parent(curve.getParent(), follicle_p)

    dynamic_curve = create_utility_node("nurbsCurve", create=follicle.outCurve)

    pm.rename(dynamic_curve.getParent(), "{}_DynamicCurve".format(setup_name))

    if hairSystem == None:

        # free index based on the fact that nothing has been disconnected in the outputObjects in a bad way.(no match)
        x = _check_free_index(nucleus.outputObjects)
        hairSystem = create_utility_node("hairSystem",
                                         active=True,
                                         collide=False,
                                         nextState=nucleus.outputObjects[x],
                                         startFrame=ctrl_master.startFrame,
                                         currentTime=time.outTime,
                                         motionDrag=ctrl_master.motionDrag)
        hairSystem.startState.connect(nucleus.inputActiveStart[x])
        hairSystem.currentState.connect(nucleus.inputActive[x])

        pm.rename(hairSystem.getParent(), "{}_nHair".format(setup_name))
        follicle.outHair.connect(hairSystem.inputHair[0])
        hairSystem.outputHair[0].connect(follicle.currentPosition)

        # the switch conditions to turn off the whole system down.
        nHair_condition_node = create_utility_node(
            "condition",
            name="nHair_condition",
            secondTerm=1,
            firstTerm=ctrl_master.springActivation
        )  # firstTerm=ctrl_master.springActivation

        nHair_condition_node.colorIfFalseR.set(1)
        nHair_condition_node.colorIfTrueR.set(3)
        nHair_condition_node.outColorR.connect(
            hairSystem.simulationMethod)  # cannot connect via utility node.

        follicle_conditon_act(follicle, ctrl_master)

    else:
        if not isinstance(hairSystem, pm.nodetypes.HairSystem):
            raise Exception("{} isn\'t a HairSystem type pymel node ".format(
                str(hairSystem)))

        x = _check_free_index(hairSystem.inputHair)
        follicle.outHair.connect(hairSystem.inputHair[x])
        hairSystem.outputHair[x].connect(follicle.currentPosition)
        follicle_conditon_act(follicle, ctrl_master)

    return dynamic_curve  # this curve should be a functionnal dynamic curve
Exemple #37
0
 def create(*args, **kwargs):
     return libRigging.create_utility_node('condition', operation=5, colorIfTrue=1.0, colorIfFalse=0.0).outColorR
    def create_stacks(self):
        """
        Create the route to compute the output transform for the avar.
        This is done using node 'stacks' which allow multiple contribution from being added while still
        be 'clear' for the rigger which layer is providing which input.
        By keeping stack seperated, we are able to keep them in isolation and use the 'worldMatrix' of their 
        leaf to easily compute the total contribution.

        This would result in the following matrix multiplications:
        1) 'offset group'
           We start with the bind transform.
           The resulting matrix is in local (also known as pre-deform) space.
        3) 'avar stack'
           This compute the desired movement from the avar values.
           Being a stack, it can be modified after creation if needed by adding layers to it.
           The resulting matrix is still in local space.
        4) 'post avar' stack'
           This add any contribution needed after the avar.
           Being a stack, it can be modified after creation if needed by adding layers to it.
           Ex: Used for having the 'all' macro avar influence other avars.
           The resulting matrix is still in local space.
        5) 'parent' group
           This matrix is identity at bind pose and will add the movement from the parent of the avar.
           The resulting matrix is in world space.

        The final transform is computed by multiplying all these stacks togheter:
        - offset.matrix
        - pre-avar.worldMatrix
        - avar.worldMatrix.
        - post-avar.worldMatrix
        - parent.matrix
        """
        nomenclature_rig = self.get_nomenclature_rig()

        # Build post-avar stack
        # This is a list of matrix multiplication that will be executed AFTER feeding the avar.
        self._stack_post = classNode.Node()
        self._stack_post.build(name=nomenclature_rig.resolve('postAvar'))
        post_stack_root = pymel.createNode(
            'transform',
            name=nomenclature_rig.resolve('postAvarRoot'),
            parent=self.grp_rig
        )
        # layer_stack_input = self._stack_post.prepend_layer(name='input')
        self._stack_post.setParent(post_stack_root)

        libRigging.connect_matrix_to_node(
            self.grp_offset.matrix,
            post_stack_root,
            name=nomenclature_rig.resolve('something')
        )

        attr_avar_model_tm = _blend_inn_matrix_attribute(
            self.model_infl._attr_out_tm,
            self.affect_tx,
            self.affect_ty,
            self.affect_tz,
            self.affect_rx,
            self.affect_ry,
            self.affect_rz,
            self.affect_sx,
            self.affect_sy,
            self.affect_sz,
        )

        # Take the result of the stack and add it on top of the bind-pose and parent group.
        self._attr_get_stack_local_tm = libRigging.create_utility_node(
            'multMatrix',
            matrixIn=(
                # self._stack_pre.worldMatrix,
                # self._stack.worldMatrix,
                # self.model_infl._attr_out_tm,
                attr_avar_model_tm,
                self._stack_post.worldMatrix,
                # self._grp_offset.matrix,
                self._grp_parent.matrix, 
            )
        ).matrixSum
        util_get_stack_local_tm = libRigging.create_utility_node(
            'decomposeMatrix',
            inputMatrix=self._attr_get_stack_local_tm
        )
        pymel.connectAttr(util_get_stack_local_tm.outputTranslate, self._grp_output.translate)
        pymel.connectAttr(util_get_stack_local_tm.outputRotate, self._grp_output.rotate)
        pymel.connectAttr(util_get_stack_local_tm.outputScale, self._grp_output.scale)
def _blend_matrix_attribute(attr_tm_a, attr_tm_b, attr_blend_tx, attr_blend_ty, attr_blend_tz, attr_blend_rx,
                            attr_blend_ry, attr_blend_rz, attr_blend_sx, attr_blend_sy, attr_blend_sz):
    # todo: replace with a matrixBlend node?
    u_decompose_a = libRigging.create_utility_node('decomposeMatrix', inputMatrix=attr_tm_a)
    u_decompose_b = libRigging.create_utility_node('decomposeMatrix', inputMatrix=attr_tm_b)
    u_compose_tm = libRigging.create_utility_node('composeMatrix')

    attr_blend_tx = libRigging.create_utility_node('blendTwoAttr', input=[u_decompose_a.outputTranslateX,
                                                                          u_decompose_b.outputTranslateX],
                                                   attributesBlender=attr_blend_tx).output
    attr_blend_ty = libRigging.create_utility_node('blendTwoAttr', input=[u_decompose_a.outputTranslateY,
                                                                          u_decompose_b.outputTranslateY],
                                                   attributesBlender=attr_blend_ty).output
    attr_blend_tz = libRigging.create_utility_node('blendTwoAttr', input=[u_decompose_a.outputTranslateZ,
                                                                          u_decompose_b.outputTranslateZ],
                                                   attributesBlender=attr_blend_tz).output
    attr_blend_rx = libRigging.create_utility_node('blendTwoAttr',
                                                   input=[u_decompose_a.outputRotateX, u_decompose_b.outputRotateX],
                                                   attributesBlender=attr_blend_rx).output
    attr_blend_ry = libRigging.create_utility_node('blendTwoAttr',
                                                   input=[u_decompose_a.outputRotateY, u_decompose_b.outputRotateY],
                                                   attributesBlender=attr_blend_ry).output
    attr_blend_rz = libRigging.create_utility_node('blendTwoAttr',
                                                   input=[u_decompose_a.outputRotateZ, u_decompose_b.outputRotateZ],
                                                   attributesBlender=attr_blend_rz).output
    attr_blend_sx = libRigging.create_utility_node('blendTwoAttr',
                                                   input=[u_decompose_a.outputScaleX, u_decompose_b.outputScaleX],
                                                   attributesBlender=attr_blend_sx).output
    attr_blend_sy = libRigging.create_utility_node('blendTwoAttr',
                                                   input=[u_decompose_a.outputScaleY, u_decompose_b.outputScaleY],
                                                   attributesBlender=attr_blend_sy).output
    attr_blend_sz = libRigging.create_utility_node('blendTwoAttr',
                                                   input=[u_decompose_a.outputScaleZ, u_decompose_b.outputScaleZ],
                                                   attributesBlender=attr_blend_sz).output

    pymel.connectAttr(attr_blend_tx, u_compose_tm.inputTranslateX)
    pymel.connectAttr(attr_blend_ty, u_compose_tm.inputTranslateY)
    pymel.connectAttr(attr_blend_tz, u_compose_tm.inputTranslateZ)
    pymel.connectAttr(attr_blend_rx, u_compose_tm.inputRotateX)
    pymel.connectAttr(attr_blend_ry, u_compose_tm.inputRotateY)
    pymel.connectAttr(attr_blend_rz, u_compose_tm.inputRotateZ)
    pymel.connectAttr(attr_blend_sx, u_compose_tm.inputScaleX)
    pymel.connectAttr(attr_blend_sy, u_compose_tm.inputScaleY)
    pymel.connectAttr(attr_blend_sz, u_compose_tm.inputScaleZ)

    return u_compose_tm.outputMatrix
Exemple #40
0
    def build_stack(self, stack, aim_target=None, **kwargs):
        nomenclature_rig = self.get_nomenclature_rig()

        # Build an aim node in-place for performance
        # This separated node allow the joints to be driven by the avars.
        aim_grp_name = nomenclature_rig.resolve('lookgrp')
        aim_grp = pymel.createNode('transform', name=aim_grp_name)

        aim_node_name = nomenclature_rig.resolve('looknode')
        aim_node = pymel.createNode('transform', name=aim_node_name)
        aim_node.setParent(aim_grp)

        aim_target_name = nomenclature_rig.resolve('target')
        aim_target = pymel.createNode('transform', name=aim_target_name)

        aim_target.setParent(aim_grp)
        self.target = aim_target

        # Build an upnode for the eyes.
        # I'm not a fan of upnodes but in this case it's better to guessing the joint orient.
        aim_upnode_name = nomenclature_rig.resolve('upnode')

        aim_upnode = pymel.createNode('transform', name=aim_upnode_name)
        #
        aim_upnode.setParent(self.grp_rig)
        pymel.parentConstraint(aim_grp, aim_upnode, maintainOffset=True)

        pymel.aimConstraint(aim_target,
                            aim_node,
                            maintainOffset=True,
                            aimVector=(0.0, 0.0, 1.0),
                            upVector=(0.0, 1.0, 0.0),
                            worldUpObject=aim_upnode,
                            worldUpType='object')

        # Position objects
        aim_grp.setParent(self._grp_offset)  # todo: add begin , end property
        aim_grp.t.set(0, 0, 0)
        aim_grp.r.set(0, 0, 0)
        jnt_tm = self.jnt.getMatrix(worldSpace=True)
        jnt_pos = jnt_tm.translate
        aim_upnode_pos = pymel.datatypes.Point(0, 1, 0) + jnt_pos
        aim_upnode.setTranslation(aim_upnode_pos, space='world')
        aim_target_pos = pymel.datatypes.Point(0, 0, 1) + jnt_pos
        aim_target.setTranslation(aim_target_pos, space='world')

        pymel.parentConstraint(aim_node, stack, maintainOffset=True)

        # Convert the rotation to avars to additional values can be added.
        util_decomposeMatrix = libRigging.create_utility_node(
            'decomposeMatrix', inputMatrix=aim_node.matrix)
        libRigging.connectAttr_withBlendWeighted(
            util_decomposeMatrix.outputTranslateX, self.attr_lr)
        libRigging.connectAttr_withBlendWeighted(
            util_decomposeMatrix.outputTranslateY, self.attr_ud)
        libRigging.connectAttr_withBlendWeighted(
            util_decomposeMatrix.outputTranslateZ, self.attr_fb)
        libRigging.connectAttr_withBlendWeighted(
            util_decomposeMatrix.outputRotateY, self.attr_yw)
        libRigging.connectAttr_withBlendWeighted(
            util_decomposeMatrix.outputRotateX, self.attr_pt)
        libRigging.connectAttr_withBlendWeighted(
            util_decomposeMatrix.outputRotateZ, self.attr_rl)
    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, **kwargs):
        super(ModelInteractiveCtrl, 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 ctrl matrix
        # It can differ from the influence to prevent the controller to appear in the geometry.
        if ctrl_tm is None:
            ctrl_tm = self.get_default_tm_ctrl()

        if ctrl_tm is None and ref_tm:
            ctrl_tm = ref_tm

        if ctrl_tm is None and self.jnt:
            ctrl_tm = self.jnt.getMatrix(worldSpace=True)

        if ctrl_tm is None:
            raise Exception("Cannot resolve ctrl transformation matrix!")

        pos_ref = self.project_pos_on_face(ctrl_tm.translate, geos=self.get_meshes())

        # 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 = libHistory.get_affected_shapes(ref)
            meshes = list(set(meshes) & set(self.rig.get_shapes()))
            if not meshes:
                meshes = set(self.rig.get_shapes())
            obj_mesh, _, out_u, out_v = libRigging.get_closest_point_on_shapes(meshes, pos_ref)

            if obj_mesh is None and follow_mesh:
                raise Exception("Can't find mesh affected by {0}. ".format(self.jnt))

        else:
            _, out_u, out_v = libRigging.get_closest_point_on_shape(obj_mesh, pos_ref)

        # Resolve u and v coordinates if necesary.
        if u_coord is None:
            u_coord = out_u
        if v_coord is None:
            v_coord = out_v

        if self.jnt:
            self.debug('Creating doritos on {0} using {1} as reference'.format(obj_mesh, self.jnt))
        else:
            self.debug('Creating doritos on {0}'.format(obj_mesh))

        # 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)

        #
        # Create the follicle setup
        #

        # Initialize external stack
        # Normally this would be hidden from animators.
        stack_name = nomenclature_rig.resolve('doritosStack')
        self._stack = Node(self)
        self._stack.build(name=stack_name)
        self._stack.setTranslation(pos_ref)

        # Create the layer_fol that will follow the geometry
        layer_fol_name = nomenclature_rig.resolve('doritosFol')
        layer_fol = self._stack.append_layer()
        layer_fol.rename(layer_fol_name)
        layer_fol.setParent(self.grp_rig)

        #
        # Constraint grp_rig
        #

        # Constraint position
        # TODO: Validate that we don't need to inverse the rotation separately.
        if parent_pos is None:
            fol_mesh = None
            if follow_mesh:
                fol_name = nomenclature_rig.resolve('follicle')
                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)
                fol_mesh.setParent(self.grp_rig)
                parent_pos = fol_mesh
            elif ref:
                parent_pos = ref

        if parent_pos:
            pymel.parentConstraint(parent_pos, layer_fol, maintainOffset=True, skipRotate=['x', 'y', 'z'])

        # Constraint rotation
        # 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.
        if parent_rot:
            pymel.orientConstraint(parent_rot, 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

        #
        # Inverse translation
        #
        if cancel_t:
            attr_ctrl_inv_t = libRigging.create_utility_node(
                'multiplyDivide', input1=self.ctrl.node.t,
                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

            layer_inv_t = self._stack.append_layer(name='inverseT')

            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_inv_t.tx)
            pymel.connectAttr(attr_doritos_ty, layer_inv_t.ty)
            pymel.connectAttr(attr_doritos_tz, layer_inv_t.tz)

        #
        # Inverse rotation
        # Add an inverse node that will counter animate the position of the ctrl.
        # TODO: Rename
        #
        if cancel_r:
            layer_inv_r = self._stack.append_layer(name='inverseR')
            # layer_doritos = pymel.createNode('transform', name=layer_doritos_name)
            # layer_doritos.setParent(self._stack.node)

            # Create inverse attributes for the ctrl

            attr_ctrl_inv_r = libRigging.create_utility_node(
                'multiplyDivide',
                input1=self.ctrl.node.r,
                input2=[-1, -1, -1]
            ).output

            pymel.connectAttr(attr_ctrl_inv_r, layer_inv_r.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

        # 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

        pymel.connectAttr(attr_ctrl_offset_sx_inn, self.ctrl.offset.scaleX)
        pymel.connectAttr(attr_ctrl_offset_sy_inn, self.ctrl.offset.scaleY)
        pymel.connectAttr(attr_ctrl_offset_sz_inn, self.ctrl.offset.scaleZ)

        # 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)

        #
        # Constraint grp_anm
        #

        # Create an output object that will hold the world position of the ctrl offset.
        # We'll use direct connections to the animation controller to simplify the dag tree and support
        # non-uniform scaling (which is really hard to do using constraints).

        # Since the the model's ctrl will still be influenced by the root ctrl, we'll need to extract the offset
        # relative to the root ctrl.
        grp_output = pymel.createNode(
            'transform',
            name=nomenclature_rig.resolve('output'),
            parent=self.grp_rig
        )

        # Position
        stack_end = self._stack.get_stack_end()
        pymel.parentConstraint(stack_end, grp_output, maintainOffset=False, skipRotate=['x', 'y', 'z'])

        # Rotation
        if parent_rot is None:
            parent_rot = stack_end
            # parent_rot = layer_inv_r.getParent()
        pymel.orientConstraint(parent_rot, grp_output, maintainOffset=True)

        # Direct-connect the output group to the ctrl offset grp.
        # Since the ctrl is a child of the master ctrl, we'll need to take it's parent in consideration.
        util_get_ctrl_offset_local_trs = libRigging.create_utility_node(
            'decomposeMatrix',
            inputMatrix=libRigging.create_utility_node(
                'multMatrix',
                matrixIn=(
                    grp_output.worldMatrix,
                    self.rig.grp_anm.worldInverseMatrix
                )
            ).matrixSum
        )
        pymel.connectAttr(util_get_ctrl_offset_local_trs.outputTranslate, self.ctrl.offset.translate)
        pymel.connectAttr(util_get_ctrl_offset_local_trs.outputRotate, self.ctrl.offset.rotate)

        # Clean dag junk
        if grp_rig:
            self._stack.setParent(grp_rig)
            if fol_mesh:
                fol_mesh.setParent(grp_rig)

        if constraint and self.jnt:
            pymel.parentConstraint(self.ctrl.node, self.jnt, maintainOffset=True)
Exemple #42
0
    def build(self, nomenclature_rig, **kwargs):
        super(SplitterNode, self).build(**kwargs)

        #
        # Create inn and out attributes.
        #
        grp_splitter_inn = pymel.createNode(
            'network', name=nomenclature_rig.resolve('udSplitterInn'))

        # The jaw opening amount in degree.
        self.attr_inn_jaw_pt = libAttr.addAttr(grp_splitter_inn, 'innJawOpen')

        # The relative uv coordinates normally sent to the follicles.
        # Note that this value is expected to change at the output of the SplitterNode (see outSurfaceU and outSurfaceV)
        self.attr_inn_surface_u = libAttr.addAttr(grp_splitter_inn,
                                                  'innSurfaceU')
        self.attr_inn_surface_v = libAttr.addAttr(grp_splitter_inn,
                                                  'innSurfaceV')

        # Use this switch to disable completely the splitter.
        self.attr_inn_bypass = libAttr.addAttr(grp_splitter_inn,
                                               'innBypassAmount')

        # The arc length in world space of the surface controlling the follicles.
        self.attr_inn_surface_range_v = libAttr.addAttr(
            grp_splitter_inn, 'innSurfaceRangeV'
        )  # How many degree does take the jaw to create 1 unit of surface deformation? (ex: 20)

        # How much inn percent is the lips following the jaw by default.
        # Note that this value is expected to change at the output of the SplitterNode (see attr_out_jaw_ratio)
        self.attr_inn_jaw_default_ratio = libAttr.addAttr(
            grp_splitter_inn, 'jawDefaultRatio')

        # The radius of the influence circle normally resolved by using the distance between the jaw and the avar as radius.
        self.attr_inn_jaw_radius = libAttr.addAttr(grp_splitter_inn,
                                                   'jawRadius')

        grp_splitter_out = pymel.createNode(
            'network', name=nomenclature_rig.resolve('udSplitterOut'))

        self.attr_out_surface_u = libAttr.addAttr(grp_splitter_out,
                                                  'outSurfaceU')
        self.attr_out_surface_v = libAttr.addAttr(grp_splitter_out,
                                                  'outSurfaceV')
        self.attr_out_jaw_ratio = libAttr.addAttr(
            grp_splitter_out, 'outJawRatio'
        )  # How much percent this influence follow the jaw after cancellation.

        #
        # Connect inn and out network nodes so they can easily be found from the SplitterNode.
        #
        attr_inn = libAttr.addAttr(grp_splitter_inn,
                                   longName='inn',
                                   attributeType='message')
        attr_out = libAttr.addAttr(grp_splitter_out,
                                   longName='out',
                                   attributeType='message')
        pymel.connectAttr(self.node.message, attr_inn)
        pymel.connectAttr(self.node.message, attr_out)

        #
        # Create node networks
        # Step 1: Get the jaw displacement in uv space (parameterV only).
        #

        attr_jaw_circumference = libRigging.create_utility_node(
            'multiplyDivide',
            name=nomenclature_rig.resolve('getJawCircumference'),
            input1X=self.attr_inn_jaw_radius,
            input2X=(math.pi * 2.0)).outputX

        attr_jaw_open_circle_ratio = libRigging.create_utility_node(
            'multiplyDivide',
            name=nomenclature_rig.resolve('getJawOpenCircleRatio'),
            operation=2,  # divide
            input1X=self.attr_inn_jaw_pt,
            input2X=360.0).outputX

        attr_jaw_active_circumference = libRigging.create_utility_node(
            'multiplyDivide',
            name=nomenclature_rig.resolve('getJawActiveCircumference'),
            input1X=attr_jaw_circumference,
            input2X=attr_jaw_open_circle_ratio).outputX

        # We need this adjustment since we cheat the influence of the avar with the plane uvs.
        # see AvarFollicle._get_follicle_relative_uv_attr for more information.
        # attr_jaw_radius_demi = libRigging.create_utility_node(
        #     'multiplyDivide',
        #     name=nomenclature_rig.resolve('getJawRangeVRange'),
        #     input1X=self.attr_inn_surface_range_v,
        #     input2X=2.0
        # ).outputX

        attr_jaw_v_range = libRigging.create_utility_node(
            'multiplyDivide',
            name=nomenclature_rig.resolve('getActiveJawRangeInSurfaceSpace'),
            operation=2,  # divide
            input1X=attr_jaw_active_circumference,
            input2X=self.attr_inn_surface_range_v).outputX

        #
        # Step 2: Resolve attr_out_jaw_ratio
        #

        # Convert attr_jaw_default_ratio in uv space.
        attr_jaw_default_ratio_v = libRigging.create_utility_node(
            'multiplyDivide',
            name=nomenclature_rig.resolve('getJawDefaultRatioUvSpace'),
            input1X=self.attr_inn_jaw_default_ratio,
            input2X=attr_jaw_v_range).outputX

        attr_jaw_uv_pos = libRigging.create_utility_node(
            'plusMinusAverage',
            name=nomenclature_rig.resolve('getCurrentJawUvPos'),
            operation=2,  # substraction
            input1D=(attr_jaw_default_ratio_v,
                     self.attr_inn_surface_v)).output1D

        attr_jaw_ratio_out = libRigging.create_utility_node(
            'multiplyDivide',
            name=nomenclature_rig.resolve('getJawRatioOut'),
            operation=2,  # division
            input1X=attr_jaw_uv_pos,
            input2X=attr_jaw_v_range).outputX

        attr_jaw_ratio_out_limited = libRigging.create_utility_node(
            'clamp',
            name=nomenclature_rig.resolve('getLimitedJawRatioOut'),
            inputR=attr_jaw_ratio_out,
            minR=0.0,
            maxR=1.0).outputR

        # Prevent division by zero
        attr_jaw_ratio_out_limited_safe = libRigging.create_utility_node(
            'condition',
            name=nomenclature_rig.resolve('getSafeJawRatioOut'),
            operation=1,  # not equal
            firstTerm=self.attr_inn_jaw_pt,
            secondTerm=0,
            colorIfTrueR=attr_jaw_ratio_out_limited,
            colorIfFalseR=self.attr_inn_jaw_default_ratio).outColorR

        #
        # Step 3: Resolve attr_out_surface_u & attr_out_surface_v
        #

        attr_inn_jaw_default_ratio_inv = libRigging.create_utility_node(
            'reverse',
            name=nomenclature_rig.resolve('getJawDefaultRatioInv'),
            inputX=self.attr_inn_jaw_default_ratio).outputX

        util_jaw_uv_default_ratio = libRigging.create_utility_node(
            'multiplyDivide',
            name=nomenclature_rig.resolve('getJawDefaultRatioUvSpace'),
            input1X=self.attr_inn_jaw_default_ratio,
            input1Y=attr_inn_jaw_default_ratio_inv,
            input2X=attr_jaw_v_range,
            input2Y=attr_jaw_v_range)
        attr_jaw_uv_default_ratio = util_jaw_uv_default_ratio.outputX
        attr_jaw_uv_default_ratio_inv = util_jaw_uv_default_ratio.outputY

        attr_jaw_uv_limit_max = libRigging.create_utility_node(
            'plusMinusAverage',
            name=nomenclature_rig.resolve('getJawSurfaceLimitMax'),
            operation=2,  # substract
            input1D=(attr_jaw_v_range, attr_jaw_uv_default_ratio_inv)).output1D

        attr_jaw_uv_limit_min = libRigging.create_utility_node(
            'plusMinusAverage',
            name=nomenclature_rig.resolve('getJawSurfaceLimitMin'),
            operation=2,  # substract
            input1D=(attr_jaw_uv_default_ratio, attr_jaw_v_range)).output1D

        attr_jaw_cancel_range = libRigging.create_utility_node(
            'clamp',
            name=nomenclature_rig.resolve('getJawCancelRange'),
            inputR=self.attr_inn_surface_v,
            minR=attr_jaw_uv_limit_min,
            maxR=attr_jaw_uv_limit_max).outputR

        attr_out_surface_v_cancelled = libRigging.create_utility_node(
            'plusMinusAverage',
            name=nomenclature_rig.resolve('getCanceledUv'),
            operation=2,  # substraction
            input1D=(self.attr_inn_surface_v, attr_jaw_cancel_range)).output1D

        #
        # Connect output attributes
        #
        attr_inn_bypass_inv = libRigging.create_utility_node(
            'reverse',
            name=nomenclature_rig.resolve('getBypassInv'),
            inputX=self.attr_inn_bypass).outputX

        # Connect output jaw_ratio
        attr_output_jaw_ratio = libRigging.create_utility_node(
            'blendWeighted',
            input=(attr_jaw_ratio_out_limited_safe,
                   self.attr_inn_jaw_default_ratio),
            weight=(attr_inn_bypass_inv, self.attr_inn_bypass)).output
        pymel.connectAttr(attr_output_jaw_ratio, self.attr_out_jaw_ratio)

        # Connect output surface u
        pymel.connectAttr(self.attr_inn_surface_u, self.attr_out_surface_u)

        # Connect output surface_v
        attr_output_surface_v = libRigging.create_utility_node(
            'blendWeighted',
            input=(attr_out_surface_v_cancelled, self.attr_inn_surface_v),
            weight=(attr_inn_bypass_inv, self.attr_inn_bypass)).output
        pymel.connectAttr(attr_output_surface_v, self.attr_out_surface_v)
Exemple #43
0
    def build(self, *args, **kwargs):
        super(Limb, self).build(*args, **kwargs)

        nomenclature_anm = self.get_nomenclature_anm()
        nomenclature_rig = self.get_nomenclature_rig()

        # Create IK system
        if not isinstance(self.sysIK, self._CLASS_SYS_IK):
            self.sysIK = self._CLASS_SYS_IK(self.chain_jnt, rig=self.rig)
        self.sysIK.name = '{0}_Ik'.format(self.name) # Hack
        self.sysIK.build(constraint=False, **kwargs)

        # Create FK system
        if not isinstance(self.sysFK, self._CLASS_SYS_FK):
            self.sysFK = self._CLASS_SYS_FK(self.chain_jnt, rig=self.rig)
        self.sysFK.name = '{0}_Fk'.format(self.name) # Hack
        self.sysFK.build(constraint=False, **kwargs)

        # Create twistbone system if needed
        if self.create_twist:
            num_twist_sys = self.sysIK.iCtrlIndex
            # If the IK system is a quad, we need to have two twist system
            for i in range(0, num_twist_sys):
                cur_sys_twist = self.sys_twist[i] if i < len(self.sys_twist) else None
                if not isinstance(cur_sys_twist, self._CLASS_SYS_TWIST):
                    cur_sys_twist = self._CLASS_SYS_TWIST(self.chain_jnt[i:(i+2)], rig=self.rig)
                    self.sys_twist.append(cur_sys_twist)
                # Hack
                twist_sys_name = self.chain_jnt[i].name().replace('_' + nomenclature_rig.type_jnt, "")
                cur_sys_twist.name = '{0}'.format(twist_sys_name)
                cur_sys_twist.build(num_twist=3, create_bend=True, **kwargs)

        # Lock X and Y axis on the elbow/knee ctrl
        if self.rig._up_axis == constants.Axis.y:
            libAttr.lock_hide_rotation(self.sysFK.ctrls[1], z=False)
        elif self.rig._up_axis == constants.Axis.z:
            libAttr.lock_hide_rotation(self.sysFK.ctrls[1], y=False)

        # Store the offset between the ik ctrl and it's joint equivalent.
        # Useful when they don't match for example on a leg setup.
        self.offset_ctrl_ik = self.sysIK.ctrl_ik.getMatrix(worldSpace=True) * self.chain_jnt[self.iCtrlIndex].getMatrix(worldSpace=True).inverse()

        # Add attributes to the attribute holder.
        # Add ikFk state attribute on the grp_rig.
        # This is currently controlled by self.ctrl_attrs.
        pymel.addAttr(self.grp_rig, longName=self.kAttrName_State, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, defaultValue=1, k=True)
        attr_ik_weight = self.grp_rig.attr(self.kAttrName_State)
        attr_fk_weight = libRigging.create_utility_node('reverse', inputX=attr_ik_weight).outputX

        # Create attribute holder (this is where the IK/FK attribute will be stored)
        # Note that this is production specific and should be defined in a sub-class implementation.
        jnt_hand = self.chain_jnt[self.sysIK.iCtrlIndex]
        ctrl_attrs_name = nomenclature_anm.resolve('atts')
        if not isinstance(self.ctrl_attrs, self._CLASS_CTRL_ATTR):
            self.ctrl_attrs = self._CLASS_CTRL_ATTR()
        self.ctrl_attrs.build(name=ctrl_attrs_name, refs=jnt_hand)
        self.ctrl_attrs.setParent(self.grp_anm)
        pymel.parentConstraint(jnt_hand, self.ctrl_attrs.offset)

        pymel.addAttr(self.ctrl_attrs, longName=self.kAttrName_State, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, defaultValue=1, k=True)
        pymel.connectAttr(self.ctrl_attrs.attr(self.kAttrName_State), self.grp_rig.attr(self.kAttrName_State))

        # Create a chain for blending ikChain and fkChain
        chain_blend = pymel.duplicate(list(self.chain_jnt), renameChildren=True, parentOnly=True)
        for input_, node in zip(self.chain_jnt, chain_blend):
            blend_nomenclature = nomenclature_rig.rebuild(input_.name())
            node.rename(blend_nomenclature.resolve('blend'))

        # Blend ikChain with fkChain
        constraint_ik_chain = self.sysIK._chain_ik
        if getattr(self.sysIK, '_chain_quad_ik', None):
            constraint_ik_chain = self.sysIK._chain_quad_ik
        for blend, oIk, oFk in zip(chain_blend, constraint_ik_chain, self.sysFK.ctrls):
            constraint = pymel.parentConstraint(oIk, oFk, blend)
            attr_weight_ik, attr_weight_fk = constraint.getWeightAliasList()
            pymel.connectAttr(attr_ik_weight, attr_weight_ik)
            pymel.connectAttr(attr_fk_weight, attr_weight_fk)
        chain_blend[0].setParent(self.grp_rig)

        #
        # Create elbow chain
        # This provide the elbow ctrl, an animator friendly way of cheating the elbow on top of the blend chain.
        #

        # Create a chain that provide the elbow controller and override the blend chain
        # (witch should only be nodes already)
        chain_elbow = pymel.duplicate(self.chain_jnt[:self.sysIK.iCtrlIndex + 1], renameChildren=True, parentOnly=True)
        for input_, node in zip(self.chain_jnt, chain_elbow):
            nomenclature_elbow = nomenclature_rig.rebuild(input_.name())
            node.rename(nomenclature_elbow.resolve('elbow'))  # todo: find a better name???
        chain_elbow[0].setParent(self.grp_rig)

        # Create elbow ctrl
        # Note that this only affect the chain until @iCtrlIndex
        for i in range(1, self.sysIK.iCtrlIndex):
            ctrl_elbow_name = nomenclature_anm.resolve('elbow{:02}'.format(i))
            ctrl_elbow_parent = chain_blend[i]
            if not isinstance(self.ctrl_elbow, self._CLASS_CTRL_ELBOW):
                self.ctrl_elbow = self._CLASS_CTRL_ELBOW(create_offset=True)
            ctrl_elbow_ref = self.chain_jnt[i]  # jnt_elbow
            self.ctrl_elbow.build(refs=ctrl_elbow_ref)
            self.ctrl_elbow.rename(ctrl_elbow_name)
            self.ctrl_elbow.setParent(self.grp_anm)
            pymel.parentConstraint(ctrl_elbow_parent, self.ctrl_elbow.offset, maintainOffset=False)
            pymel.pointConstraint(chain_blend[0], chain_elbow[0], maintainOffset=False)
            pymel.aimConstraint(self.ctrl_elbow, chain_elbow[i-1], worldUpType=2,
                                worldUpObject=chain_blend[i-1])  # Object Rotation Up
            pymel.aimConstraint(chain_blend[i+1], chain_elbow[i], worldUpType=2,
                                worldUpObject=chain_blend[i])  # Object Rotation Up
            pymel.pointConstraint(self.ctrl_elbow, chain_elbow[i], maintainOffset=False)
        # Constraint the last elbow joint on the blend joint at the ctrl index
        pymel.parentConstraint(chain_blend[self.sysIK.iCtrlIndex], chain_elbow[self.sysIK.iCtrlIndex])

        # Constraint input chain
        # Note that we only constraint to the elbow chain until @iCtrlIndex.
        # Afterward we constraint to the blend chain.
        for i in range(self.sysIK.iCtrlIndex):
            inn = self.chain_jnt[i]
            ref = chain_elbow[i]
            pymel.parentConstraint(ref, inn, maintainOffset=True)  # todo: set to maintainOffset=False?
        for i in range(self.sysIK.iCtrlIndex, len(self.chain_jnt)):
            inn = self.chain_jnt[i]
            ref = chain_blend[i]
            pymel.parentConstraint(ref, inn, maintainOffset=True)  # todo: set to maintainOffset=False?

        # Connect visibility
        pymel.connectAttr(attr_ik_weight, self.sysIK.grp_anm.visibility)
        pymel.connectAttr(attr_fk_weight, self.sysFK.grp_anm.visibility)

        # Connect globalScale
        pymel.connectAttr(self.grp_rig.globalScale, self.sysIK.grp_rig.globalScale, force=True)
        self.globalScale = self.grp_rig.globalScale  # Expose the attribute, the rig will reconise it.

        # Parent sub-modules so they are affected by displayLayer assignment and such.
        self.sysIK.grp_anm.setParent(self.grp_anm)
        self.sysIK.grp_rig.setParent(self.grp_rig)
        self.sysFK.grp_anm.setParent(self.grp_anm)
        for sys_twist in self.sys_twist:
            if sys_twist.create_bend:
                sys_twist.grp_anm.setParent(self.grp_anm)
            sys_twist.grp_rig.setParent(self.grp_rig)

        self.attState = attr_ik_weight  # Expose state
Exemple #44
0
    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)
Exemple #45
0
    def build(self, rig, *args, **kwargs):
        super(Limb, self).build(rig, *args, **kwargs)

        nomenclature_anm = self.get_nomenclature_anm(rig)
        nomenclature_rig = self.get_nomenclature_rig(rig)

        # Create IK system
        if not isinstance(self.sysIK, self._CLASS_SYS_IK):
            self.sysIK = self._CLASS_SYS_IK(self.chain_jnt)
        self.sysIK.name = '{0}_Ik'.format(self.name) # Hack
        self.sysIK.build(rig, constraint=False, **kwargs)

        # Create FK system
        if not isinstance(self.sysFK, self._CLASS_SYS_FK):
            self.sysFK = self._CLASS_SYS_FK(self.chain_jnt)
        self.sysFK.name = '{0}_Fk'.format(self.name) # Hack
        self.sysFK.build(rig, constraint=False, **kwargs)

        #Lock X and Y axis on the elbow/knee ctrl
        libAttr.lock_hide_rotation(self.sysFK.ctrls[1], z=False)

        # Store the offset between the ik ctrl and it's joint equivalent.
        # Useful when they don't match for example on a leg setup.
        self.offset_ctrl_ik = self.sysIK.ctrl_ik.getMatrix(worldSpace=True) * self.chain_jnt[self.iCtrlIndex].getMatrix(worldSpace=True).inverse()

        # Add attributes to the attribute holder.
        # Add ikFk state attribute on the grp_rig.
        # This is currently controlled by self.ctrl_attrs.
        pymel.addAttr(self.grp_rig, longName=self.kAttrName_State, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, defaultValue=1, k=True)
        attr_ik_weight = self.grp_rig.attr(self.kAttrName_State)
        attr_fk_weight = libRigging.create_utility_node('reverse', inputX=attr_ik_weight).outputX

        # Create attribute holder (this is where the IK/FK attribute will be stored)
        # Note that this is production specific and should be defined in a sub-class implementation.
        jnt_hand = self.chain_jnt[self.sysIK.iCtrlIndex]
        ctrl_attrs_name = nomenclature_anm.resolve('atts')
        if not isinstance(self.ctrl_attrs, self._CLASS_CTRL_ATTR):
            self.ctrl_attrs = self._CLASS_CTRL_ATTR()
        self.ctrl_attrs.build(rig, name=ctrl_attrs_name, refs=jnt_hand)
        self.ctrl_attrs.setParent(self.grp_anm)
        pymel.parentConstraint(jnt_hand, self.ctrl_attrs.offset)

        pymel.addAttr(self.ctrl_attrs, longName=self.kAttrName_State, hasMinValue=True, hasMaxValue=True, minValue=0, maxValue=1, defaultValue=1, k=True)
        pymel.connectAttr(self.ctrl_attrs.attr(self.kAttrName_State), self.grp_rig.attr(self.kAttrName_State))

        # Create a chain for blending ikChain and fkChain
        chain_blend = pymel.duplicate(list(self.chain_jnt), renameChildren=True, parentOnly=True)
        for input_, node in zip(self.chain_jnt, chain_blend):
            blend_nomenclature = nomenclature_rig.rebuild(input_.name())
            node.rename(blend_nomenclature.resolve('blend'))

        # Blend ikChain with fkChain
        for blend, oIk, oFk in zip(chain_blend, self.sysIK._chain_ik, self.sysFK.ctrls):
            constraint = pymel.parentConstraint(oIk, oFk, blend)
            attr_weight_ik, attr_weight_fk = constraint.getWeightAliasList()
            pymel.connectAttr(attr_ik_weight, attr_weight_ik)
            pymel.connectAttr(attr_fk_weight, attr_weight_fk)
        chain_blend[0].setParent(self.grp_rig)

        #
        # Create elbow chain
        # This provide the elbow ctrl, an animator friendly way of cheating the elbow on top of the blend chain.
        #

        # Create a chain that provide the elbow controller and override the blend chain
        # (witch should only be nodes already)
        chain_elbow = pymel.duplicate(self.chain_jnt[:self.sysIK.iCtrlIndex], renameChildren=True, parentOnly=True)
        for input_, node in zip(self.chain_jnt, chain_elbow):
            nomenclature_elbow = nomenclature_rig.rebuild(input_.name())
            node.rename(nomenclature_elbow.resolve('elbow'))  # todo: find a better name???
        chain_elbow[0].setParent(self.grp_rig)

        # Create elbow ctrl
        # Note that this only affect the chain until @iCtrlIndex
        index_elbow = 1
        ctrl_elbow_name = nomenclature_anm.resolve('elbow')
        ctrl_elbow_parent = chain_blend[index_elbow]
        if not isinstance(self.ctrl_elbow, self._CLASS_CTRL_ELBOW):
            self.ctrl_elbow = self._CLASS_CTRL_ELBOW(create_offset=True)
        ctrl_elbow_ref = self.chain_jnt[self.iCtrlIndex - 1]  # jnt_elbow
        self.ctrl_elbow.build(rig, refs=ctrl_elbow_ref)
        self.ctrl_elbow.rename(ctrl_elbow_name)
        self.ctrl_elbow.setParent(self.grp_anm)
        pymel.parentConstraint(ctrl_elbow_parent, self.ctrl_elbow.offset, maintainOffset=False)

        pymel.pointConstraint(chain_blend[0], chain_elbow[0], maintainOffset=False)
        pymel.aimConstraint(self.ctrl_elbow, chain_elbow[0], worldUpType=2,
                            worldUpObject=chain_blend[0])  # Object Rotation Up
        pymel.aimConstraint(chain_blend[self.sysIK.iCtrlIndex], chain_elbow[index_elbow], worldUpType=2,
                            worldUpObject=chain_blend[index_elbow])  # Object Rotation Up
        pymel.pointConstraint(self.ctrl_elbow, chain_elbow[index_elbow], maintainOffset=False)

        # Constraint input chain
        # Note that we only constraint to the elbow chain until @iCtrlIndex.
        # Afterward we constraint to the blend chain.
        for i in range(self.sysIK.iCtrlIndex):
            inn = self.chain_jnt[i]
            ref = chain_elbow[i]
            pymel.parentConstraint(ref, inn, maintainOffset=True)  # todo: set to maintainOffset=False?
        for i in range(self.sysIK.iCtrlIndex, len(self.chain_jnt)):
            inn = self.chain_jnt[i]
            ref = chain_blend[i]
            pymel.parentConstraint(ref, inn, maintainOffset=True)  # todo: set to maintainOffset=False?

        # Connect visibility
        pymel.connectAttr(attr_ik_weight, self.sysIK.grp_anm.visibility)
        pymel.connectAttr(attr_fk_weight, self.sysFK.grp_anm.visibility)

        # Connect globalScale
        pymel.connectAttr(self.grp_rig.globalScale, self.sysIK.grp_rig.globalScale, force=True)
        self.globalScale = self.grp_rig.globalScale  # Expose the attribute, the rig will reconise it.

        # Parent sub-modules so they are affected by displayLayer assignment and such.
        self.sysIK.grp_anm.setParent(self.grp_anm)
        self.sysIK.grp_rig.setParent(self.grp_rig)
        self.sysFK.grp_anm.setParent(self.grp_anm)

        self.attState = attr_ik_weight  # Expose state
Exemple #46
0
 def create(arg1, arg2):
     u = libRigging.create_utility_node('multiplyDivide', input1X=arg1, input2X=arg2)
     u.operation.set(2)  # HACK: Prevent division by zero by changing the operator at the last second.
     return u.outputX
Exemple #47
0
    def build(self, *args, **kwargs):
        super(Hand, self).build(parent=False, *args, **kwargs)

        # Resolve fingers and metacarpals
        #jnts_metacarpals = []
        #metacarpals_sys = []
        meta_index = 0 #We can have less metacarpals than finger chains

        nomenclature_anm = self.get_nomenclature_anm()
        nomenclature_rig = self.get_nomenclature_rig()

        # Create fingers systems if necessary
        for i, chain in enumerate(self.chains):
            chain_length = len(chain)

            # Skip unsupported chain length
            if chain_length > 5:
                logging.warning("Unsupported chain length for {0}. Expected 4 or less, got {1}".format(
                        chain, chain_length
                ))
                continue

            # Resolve phalanges and metacarpal from chain
            if chain_length == 5:
                jnt_metacarpal = chain[0]
                jnts_phalanges = chain[1:-1]
                #jnts_metacarpals.append(jnt_metacarpal)
            else:
                jnt_metacarpal = None
                jnts_phalanges = chain[:-1]

            # Rig metacarpals if necessary
            ctrl_meta = None
            if jnt_metacarpal:
                if meta_index >= len(self.fk_sys_metacarpals):
                    ctrl_meta = rigFK.FK([jnt_metacarpal], rig=self.rig)
                    ctrl_meta.name = ctrl_meta.get_default_name()
                    self.fk_sys_metacarpals.append(ctrl_meta)

                ctrl_meta = self.fk_sys_metacarpals[meta_index]
                ctrl_meta.create_spaceswitch = False
                ctrl_meta.build()
                ctrl_meta.grp_anm.setParent(self.grp_anm)
                meta_index += 1

            # Rig fingers
            if not self.sysFingers or i >= len(self.sysFingers):
                sysFinger = rigFK.AdditiveFK(jnts_phalanges, rig=self.rig)
                sysFinger.name = sysFinger.get_default_name()
                self.sysFingers.append(sysFinger)

            sysFinger = self.sysFingers[i]
            sysFinger.create_spaceswitch = False
            sysFinger.build()
            if ctrl_meta:
                sysFinger.grp_anm.setParent(ctrl_meta.ctrls[0])
            else:
                sysFinger.grp_anm.setParent(self.grp_anm)

            #Keep the system to make sure it match the index of the metacarpal associated
            #metacarpals_sys.append(sysFinger)

        # Rig the 'cup' setup
        if self.fk_sys_metacarpals:
            pos_inn = self.fk_sys_metacarpals[0].ctrls[0].getTranslation(space='world')
            pos_out = self.fk_sys_metacarpals[-1].ctrls[0].getTranslation(space='world')
            pos_mid = ((pos_out - pos_inn) / 2.0) + pos_inn

            # Resolve the metacarpal plane orientation
            parent_tm = self.parent.getMatrix(worldSpace=True)
            x = pos_out - pos_inn
            x.normalize()
            y = pymel.datatypes.Vector(parent_tm.a10, parent_tm.a11, parent_tm.a12)
            z = x.cross(y) #Get the front vector
            z.normalize()
            y = x.cross(z) #Ensure the up vector is orthogonal
            y.normalize()
            ref_tm = pymel.datatypes.Matrix(
                    z.x, z.y, z.z, 0.0,
                    y.x, y.y, y.z, 0.0,
                    x.x, x.y, x.z, 0.0,
                    pos_mid.x, pos_mid.y, pos_mid.z, 1.0
            )
            rig_metacarpal_center = pymel.spaceLocator(name=nomenclature_rig.resolve('metacarpCenter'))
            rig_metacarpal_center.setMatrix(ref_tm)
            rig_metacarpal_center.setParent(self.grp_rig)
            pymel.parentConstraint(self.parent, rig_metacarpal_center, maintainOffset=True)

            # Create the 'cup' attribute
            attr_holder = self.grp_anm
            pymel.addAttr(attr_holder, longName='cup', keyable=True)
            attr_cup = attr_holder.attr('cup')


            for i, ctrl_metacarpal in enumerate(self.fk_sys_metacarpals):
                width = pos_inn.distanceTo(pos_out)
                pos = ctrl_metacarpal.ctrls[0].getTranslation(space='world')
                ratio = (pos - pos_inn).length() / width

                grp_pivot_name = nomenclature_rig.resolve(ctrl_metacarpal.input[0].name() + '_pivot')
                grp_pivot = pymel.createNode('transform', name=grp_pivot_name)
                grp_pivot.setMatrix(ref_tm)
                grp_pivot.setParent(rig_metacarpal_center)

                multiplier = (ratio * 2.0) - 1.0
                attr_rotate_x = libRigging.create_utility_node('multiplyDivide',
                                                               input1X=attr_cup,
                                                               input2X=multiplier,
                                                               ).outputX
                pymel.connectAttr(attr_rotate_x, grp_pivot.rotateY)

                # jnt_phalange_inn = next(iter(jnt_metacarpal.getChildren()))
                # grp_pos = pymel.createNode('transform')
                # grp_pos.rename(nomenclature_rig.resolve(jnt_phalange_inn.name() + "_parentPos"))
                # grp_pos.setMatrix(jnt_phalange_inn.getMatrix(worldSpace=True))
                # grp_pos.setParent(grp_pivot)

                # Note that the cup system worked with a partial parentConstraint.
                # I've remove it since it was breaking things.
                # TODO: Make it work again.
                pymel.parentConstraint(grp_pivot, ctrl_metacarpal.ctrls[0].offset, maintainOffset=True)

                # HACK: Override the phalanges rig parent
                # sysFinger = metacarpals_sys[i]
                # pymel.disconnectAttr(sysFinger.grp_anm.translateX)
                # pymel.disconnectAttr(sysFinger.grp_anm.translateY)
                # pymel.disconnectAttr(sysFinger.grp_anm.translateZ)
                # pymel.parentConstraint(grp_pivot, sysFinger.grp_anm, maintainOffset=True, skipRotate=['x', 'y', 'z'])
                # pymel.pointConstraint(grp_pos, sysFinger.grp_anm, maintainOffset=True)

            #Connect Global scale
            pymel.connectAttr(self.grp_rig.globalScale, self.grp_rig.scaleX)
            pymel.connectAttr(self.grp_rig.globalScale, self.grp_rig.scaleY)
            pymel.connectAttr(self.grp_rig.globalScale, self.grp_rig.scaleZ)

        pymel.parentConstraint(self.parent, self.grp_anm, maintainOffset=True)
Exemple #48
0
 def create(arg1, arg2):
     return libRigging.create_utility_node('plusMinusAverage', operation=2, input1D=[arg1, arg2]).output1D
Exemple #49
0
    def build(self, **kwargs):
        """
        Build function for the softik node
        :return: Nothing
        """
        super(SoftIkNode, self).build(**kwargs)
        formula = libFormula.Formula()
        fn_add_attr = functools.partial(libAttr.addAttr,
                                        self.node,
                                        hasMinValue=True,
                                        hasMaxValue=True)
        formula.inMatrixS = fn_add_attr(longName='inMatrixS', dt='matrix')
        formula.inMatrixE = fn_add_attr(longName='inMatrixE', dt='matrix')
        formula.inRatio = fn_add_attr(longName='inRatio', at='float')
        formula.inStretch = fn_add_attr(longName='inStretch', at='float')
        formula.inChainLength = fn_add_attr(longName='inChainLength',
                                            at='float',
                                            defaultValue=1.0)

        # inDistance is the distance between the start of the chain and the ikCtrl
        formula.inDistance = "inMatrixS~inMatrixE"
        # distanceSoft is the distance before distanceMax where the softIK kick in.
        # ex: For a chain of length 10.0 with a ratio of 0.1, the distanceSoft will be 1.0.
        formula.distanceSoft = "inChainLength*inRatio"
        # distanceSafe is the distance where there's no softIK.
        # ex: For a chain of length 10.0 with a ratio of 0.1, the distanceSafe will be 9.0.
        formula.distanceSafe = "inChainLength-distanceSoft"
        # This represent the soft-ik state
        # When the soft-ik kick in, the value is 0.0.
        # When the stretch kick in, the value is 1.0.
        # |-----------|-----------|----------|
        # -1          0.0         1.0         +++
        # -dBase      dSafe       dMax
        # Hack: Prevent potential division by zero.
        # Originally we were using a condition, however in Maya 2016+ in Parallel or Serial evaluation mode, this
        # somehow evalated the division even when the condition was False.
        formula.distanceSoftClamped = libRigging.create_utility_node(
            'clamp', inputR=formula.distanceSoft, minR=0.0001,
            maxR=999).outputR
        formula.deltaSafeSoft = "(inDistance-distanceSafe)/distanceSoftClamped"

        # outDistanceSoft is the desired ikEffector distance from the chain start after aplying the soft-ik
        # If there's no stretch, this will be directly applied to the ikEffector.
        # If there's stretch, this will be used to compute the amount of stretch needed to reach the ikCtrl
        # while preserving the shape.
        formula.outDistanceSoft = "(distanceSoft*(1-(e^(deltaSafeSoft*-1))))+distanceSafe"

        # Affect ikEffector distance only where inDistance if bigger than distanceSafe.
        formula.outDistance = libRigging.create_utility_node(
            'condition',
            operation=2,
            firstTerm=formula.deltaSafeSoft,
            secondTerm=0.0,
            colorIfTrueR=formula.outDistanceSoft,
            colorIfFalseR=formula.inDistance).outColorR
        # Affect ikEffector when we're not using stretching
        formula.outDistance = libRigging.create_utility_node(
            'blendTwoAttr',
            input=[formula.outDistance, formula.inDistance],
            attributesBlender=formula.inStretch).output

        #
        # Handle Stretching
        #

        # If we're using softIk AND stretchIk, we'll use the outRatioSoft to stretch the joints enough so
        # that the ikEffector reach the ikCtrl.
        formula.outStretch = "inDistance/outDistanceSoft"

        # Apply the softIK only AFTER the distanceSafe
        formula.outStretch = libRigging.create_utility_node(
            'condition',
            operation=2,
            firstTerm=formula.inDistance,
            secondTerm=formula.distanceSafe,
            colorIfTrueR=formula.outStretch,
            colorIfFalseR=1.0).outColorR

        # Apply stretching only if inStretch is ON
        formula.outStretch = libRigging.create_utility_node(
            'blendTwoAttr',
            input=[1.0, formula.outStretch],
            attributesBlender=formula.inStretch).output

        #
        # Connect outRatio and outStretch to our softIkNode
        #
        # fnAddAttr(longName='outTranslation', dt='float3')
        formula.outRatio = "outDistance/inDistance"
        attr_ratio = fn_add_attr(longName='outRatio', at='float')
        pymel.connectAttr(formula.outRatio, attr_ratio)

        attr_stretch = fn_add_attr(longName='outStretch', at='float')
        pymel.connectAttr(formula.outStretch, attr_stretch)
Exemple #50
0
    def setup_softik(self, ik_handle_to_constraint, stretch_chain):
        """
        Setup the softik system a ik system
        :param ik_handle_to_constraint: The ik handle to constraint on the soft ik network (Can be more than one)
        :param stretch_chain: The chain on which the stretch will be connected
        :return: Nothing
        """
        nomenclature_rig = self.get_nomenclature_rig()

        oAttHolder = self.ctrl_ik
        fnAddAttr = functools.partial(libAttr.addAttr,
                                      hasMinValue=True,
                                      hasMaxValue=True)
        attInRatio = fnAddAttr(oAttHolder,
                               longName='softIkRatio',
                               niceName='SoftIK',
                               defaultValue=0,
                               minValue=0,
                               maxValue=50,
                               k=True)
        attInStretch = fnAddAttr(oAttHolder,
                                 longName='stretch',
                                 niceName='Stretch',
                                 defaultValue=0,
                                 minValue=0,
                                 maxValue=1.0,
                                 k=True)
        # Adjust the ratio in percentage so animators understand that 0.03 is 3%
        attInRatio = libRigging.create_utility_node('multiplyDivide',
                                                    input1X=attInRatio,
                                                    input2X=0.01).outputX

        # Create and configure SoftIK solver
        soft_ik_network_name = nomenclature_rig.resolve('softik')
        soft_ik_network = SoftIkNode()
        soft_ik_network.build(name=soft_ik_network_name)
        soft_ik_network.setParent(self.grp_rig)

        pymel.connectAttr(attInRatio, soft_ik_network.inRatio)
        pymel.connectAttr(attInStretch, soft_ik_network.inStretch)
        pymel.connectAttr(self._ikChainGrp.worldMatrix,
                          soft_ik_network.inMatrixS)
        pymel.connectAttr(self._ik_handle_target.worldMatrix,
                          soft_ik_network.inMatrixE)
        attr_distance = libFormula.parse('distance*globalScale',
                                         distance=self.chain_length,
                                         globalScale=self.grp_rig.globalScale)
        pymel.connectAttr(attr_distance, soft_ik_network.inChainLength)

        attOutRatio = soft_ik_network.outRatio
        attOutRatioInv = libRigging.create_utility_node(
            'reverse', inputX=soft_ik_network.outRatio).outputX
        #TODO: Improve softik ratio when using multiple ik handle. Not the same ratio will be used depending of the angle
        for handle in ik_handle_to_constraint:
            pointConstraint = pymel.pointConstraint(self._ik_handle_target,
                                                    self._ikChainGrp, handle)
            pointConstraint.rename(pointConstraint.name().replace(
                'pointConstraint', 'softIkConstraint'))
            weight_inn, weight_out = pointConstraint.getWeightAliasList()[
                -2:]  #Ensure to get the latest target added
            pymel.connectAttr(attOutRatio, weight_inn)
            pymel.connectAttr(attOutRatioInv, weight_out)

        # Connect stretch
        for i in range(1, self.iCtrlIndex + 1):
            obj = stretch_chain[i]
            util_get_t = libRigging.create_utility_node(
                'multiplyDivide',
                input1X=soft_ik_network.outStretch,
                input1Y=soft_ik_network.outStretch,
                input1Z=soft_ik_network.outStretch,
                input2=obj.t.get())
            pymel.connectAttr(util_get_t.outputX, obj.tx, force=True)
            pymel.connectAttr(util_get_t.outputY, obj.ty, force=True)
            pymel.connectAttr(util_get_t.outputZ, obj.tz, force=True)

        return soft_ik_network
Exemple #51
0
 def create(arg1, arg2):
     return libRigging.create_utility_node('multiplyDivide', operation=3, input1X=arg1, input2X=arg2).outputX
Exemple #52
0
 def create(arg1, arg2):
     log.debug('[equal:create] {0} * {1}'.format(arg1, arg2))
     return libRigging.create_utility_node('condition', operation=0, colorIfTrue=1.0, colorIfFalse=0.0).outColorR
    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)
Exemple #54
0
    def create_spaceswitch(self,
                           module,
                           parent,
                           add_default=True,
                           default_name=None,
                           add_world=False,
                           **kwargs):
        """
        Create the space switch attribute on the controller using a list of target found from it's module hierarchy
        :param module: The module on which we want to process space switch targets
        :param parent: The parent used as the default (local) target
        :param add_default: Is the default target will be added to the list of targets
        :param default_name: The name of the default target
        :param add_world: Is the world will be added as a target
        :param kwargs: Additional parameters
        :return: None
        """
        # TODO: Handle when parent is None?
        nomenclature = module.rig.nomenclature

        # Resolve spaceswitch targets
        targets, labels, indexes = self.get_spaceswitch_targets(
            module, parent, add_world=add_world, add_local=add_default)
        if not targets:
            module.warning(
                "Can't add space switch on {0}. No targets found!".format(
                    self.node.__melobject__()))
            return

        if default_name is None:
            default_name = 'Local'

        # Resolve the niceName of the targets
        for i in range(len(targets)):
            target = targets[i]
            label = labels[i]

            if label is None and target is not None:
                name = nomenclature(target.name())
                name.remove_extra_tokens()
                labels[i] = name.resolve()

        # Create the parent constraint before adding the local since local target will be set to itself
        # to keep a serialized link to the local target
        layer_space_switch = self.append_layer('spaceSwitch')
        parent_constraint = pymel.parentConstraint(targets,
                                                   layer_space_switch,
                                                   maintainOffset=True,
                                                   **kwargs)

        # Build the enum string from the information we got
        enum_string = ""
        # Add the local option if needed
        if add_default:
            # We cannot self referencing since it will break maya deletion mechanism
            # targets.append(self)
            # indexes.append(default_index)
            # labels.append(default_name)
            enum_string += default_name + "=" + \
                           str(self.local_index)

        # The enum string will skip index if needed
        for label, index in zip(labels, indexes):
            if enum_string:
                enum_string += ":"
            enum_string += label + "=" + str(index)

        # Update the serialized variable to make sure everything is up to date
        for i, target in enumerate(targets):
            if target not in self.targets:
                self.targets.append(target)
                if indexes[i] in self.targets_indexes:
                    log.warning(
                        "Index ({0}) is already used for space switch on ctrl {1}. "
                        "Strange behavior could happen".format(
                            indexes[i], self.name()))
                self.targets_indexes.append(indexes[i])

        attr_space = libAttr.addAttr(self.node,
                                     'space',
                                     at='enum',
                                     enumName=enum_string,
                                     k=True)
        atts_weights = parent_constraint.getWeightAliasList()

        for i, att_weight in enumerate(atts_weights):
            index_to_match = indexes[i]
            att_enabled = libRigging.create_utility_node(  #Equal
                'condition',
                firstTerm=attr_space,
                secondTerm=index_to_match,
                colorIfTrueR=1,
                colorIfFalseR=0).outColorR
            pymel.connectAttr(att_enabled, att_weight)

        # By Default, the active space will be local, else root and finally fallback on the first index found
        if add_default:
            self.node.space.set(default_name)
        elif self._reserved_idx['root'] in self.targets_indexes:
            self.node.space.set(self._reserved_idx['root'])
        else:
            if self.targets_indexes:
                self.node.space.set(self.targets_indexes[0])
Exemple #55
0
    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)
Exemple #56
0
    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)
Exemple #57
0
    def build_stack(self, stack, mult_u=1.0, mult_v=1.0):
        """
        The dag stack is a chain of transform nodes daisy chained together that computer the final transformation of the influence.
        The decision of using transforms instead of multMatrix nodes is for clarity.
        Note also that because of it's parent (the offset node) the stack relative to the influence original translation.
        """
        # TODO: Maybe use sub-classing to differenciate when we need to use a surface or not.
        nomenclature_rig = self.get_nomenclature_rig()

        #
        # Extract the base U and V of the base influence using the stack parent. (the 'offset' node)
        #
        surface_shape = self.surface.getShape()

        util_get_base_uv_absolute = libRigging.create_utility_node(
            'closestPointOnSurface',
            inPosition=self._grp_offset.t,
            inputSurface=surface_shape.worldSpace)

        util_get_base_uv_normalized = libRigging.create_utility_node(
            'setRange',
            oldMinX=surface_shape.minValueU,
            oldMaxX=surface_shape.maxValueU,
            oldMinY=surface_shape.minValueV,
            oldMaxY=surface_shape.maxValueV,
            minX=0,
            maxX=1,
            minY=0,
            maxY=1,
            valueX=util_get_base_uv_absolute.parameterU,
            valueY=util_get_base_uv_absolute.parameterV)
        attr_base_u_normalized = util_get_base_uv_normalized.outValueX
        attr_base_v_normalized = util_get_base_uv_normalized.outValueY

        self._attr_u_base = libAttr.addAttr(
            self.grp_rig,
            longName=self._ATTR_NAME_U_BASE,
            defaultValue=attr_base_u_normalized.get())
        self._attr_v_base = libAttr.addAttr(
            self.grp_rig,
            longName=self._ATTR_NAME_V_BASE,
            defaultValue=attr_base_v_normalized.get())

        pymel.connectAttr(attr_base_u_normalized,
                          self.grp_rig.attr(self._ATTR_NAME_U_BASE))
        pymel.connectAttr(attr_base_v_normalized,
                          self.grp_rig.attr(self._ATTR_NAME_V_BASE))

        #
        # Create follicle setup
        # The setup is composed of two follicles.
        # One for the "bind pose" and one "driven" by the avars..
        # The delta between the "bind pose" and the "driven" follicles is then applied to the influence.
        #

        # Determine the follicle U and V on the reference nurbsSurface.
        # jnt_pos = self.jnt.getTranslation(space='world')
        # fol_pos, fol_u, fol_v = libRigging.get_closest_point_on_surface(self.surface, jnt_pos)
        base_u_val = self._attr_u_base.get()
        base_v_val = self._attr_v_base.get()

        # Resolve the length of each axis of the surface
        self._attr_length_u, self._attr_length_v, arcdimension_shape = libRigging.create_arclengthdimension_for_nurbsplane(
            self.surface)
        arcdimension_transform = arcdimension_shape.getParent()
        arcdimension_transform.rename(nomenclature_rig.resolve('arcdimension'))
        arcdimension_transform.setParent(self.grp_rig)

        #
        # Create two follicle.
        # - influenceFollicle: Affected by the ud and lr Avar
        # - bindPoseFollicle: A follicle that stay in place and keep track of the original position.
        # We'll then compute the delta of the position of the two follicles.
        # This allow us to move or resize the plane without affecting the built rig. (if the rig is in neutral pose)
        #
        offset_name = nomenclature_rig.resolve('bindPoseRef')
        obj_offset = pymel.createNode('transform', name=offset_name)
        obj_offset.setParent(self._grp_offset)

        fol_offset_name = nomenclature_rig.resolve('bindPoseFollicle')
        # fol_offset = libRigging.create_follicle(obj_offset, self.surface, name=fol_offset_name)
        fol_offset_shape = libRigging.create_follicle2(self.surface,
                                                       u=base_u_val,
                                                       v=base_v_val)
        fol_offset = fol_offset_shape.getParent()
        fol_offset.rename(fol_offset_name)
        pymel.parentConstraint(fol_offset, obj_offset, maintainOffset=False)
        fol_offset.setParent(self.grp_rig)

        # Create the influence follicle
        influence_name = nomenclature_rig.resolve('influenceRef')
        influence = pymel.createNode('transform', name=influence_name)
        influence.setParent(self._grp_offset)

        fol_influence_name = nomenclature_rig.resolve('influenceFollicle')
        fol_influence_shape = libRigging.create_follicle2(self.surface,
                                                          u=base_u_val,
                                                          v=base_v_val)
        fol_influence = fol_influence_shape.getParent()
        fol_influence.rename(fol_influence_name)
        pymel.parentConstraint(fol_influence, influence, maintainOffset=False)
        fol_influence.setParent(self.grp_rig)

        #
        # Extract the delta of the influence follicle and it's initial pose follicle
        #
        attr_localTM = libRigging.create_utility_node(
            'multMatrix',
            matrixIn=[influence.worldMatrix,
                      obj_offset.worldInverseMatrix]).matrixSum

        # Since we are extracting the delta between the influence and the bindpose matrix, the rotation of the surface
        # is not taken in consideration wich make things less intuitive for the rigger.
        # So we'll add an adjustement matrix so the rotation of the surface is taken in consideration.
        util_decomposeTM_bindPose = libRigging.create_utility_node(
            'decomposeMatrix', inputMatrix=obj_offset.worldMatrix)
        attr_translateTM = libRigging.create_utility_node(
            'composeMatrix',
            inputTranslate=util_decomposeTM_bindPose.outputTranslate
        ).outputMatrix
        attr_translateTM_inv = libRigging.create_utility_node(
            'inverseMatrix',
            inputMatrix=attr_translateTM,
        ).outputMatrix
        attr_rotateTM = libRigging.create_utility_node(
            'multMatrix',
            matrixIn=[obj_offset.worldMatrix, attr_translateTM_inv]).matrixSum
        attr_rotateTM_inv = libRigging.create_utility_node(
            'inverseMatrix', inputMatrix=attr_rotateTM).outputMatrix
        attr_finalTM = libRigging.create_utility_node(
            'multMatrix',
            matrixIn=[attr_rotateTM_inv, attr_localTM,
                      attr_rotateTM]).matrixSum

        util_decomposeTM = libRigging.create_utility_node(
            'decomposeMatrix', inputMatrix=attr_finalTM)

        #
        # Resolve the parameterU and parameterV
        #
        self._attr_u_mult_inn = libAttr.addAttr(
            self.grp_rig,
            longName=self._ATTR_NAME_U_MULT,
            defaultValue=self._AVAR_DEFAULT_MULTIPLIER_U)
        self._attr_v_mult_inn = libAttr.addAttr(
            self.grp_rig,
            longName=self._ATTR_NAME_V_MULT,
            defaultValue=self._AVAR_DEFAULT_MULTIPLIER_V)
        attr_u_inn, attr_v_inn = self._get_follicle_absolute_uv_attr()

        #
        # Create the 1st (follicleLayer) that will contain the extracted position from the ud and lr Avar.
        #
        layer_follicle = stack.append_layer('follicleLayer')
        pymel.connectAttr(util_decomposeTM.outputTranslate,
                          layer_follicle.translate)

        pymel.connectAttr(attr_u_inn, fol_influence.parameterU)
        pymel.connectAttr(attr_v_inn, fol_influence.parameterV)
        pymel.connectAttr(self._attr_u_base, fol_offset.parameterU)
        pymel.connectAttr(self._attr_v_base, fol_offset.parameterV)

        #
        # The second layer (oobLayer for out-of-bound) that allow the follicle to go outside it's original plane.
        # If the UD value is out the nurbsPlane UV range (0-1), ie 1.1, we'll want to still offset the follicle.
        # For that we'll compute a delta between a small increment (0.99 and 1.0) and multiply it.
        #
        nomenclature_rig = self.get_nomenclature_rig()
        oob_step_size = 0.001  # TODO: Expose a Maya attribute?

        fol_clamped_v_name = nomenclature_rig.resolve('influenceClampedV')
        fol_clamped_v_shape = libRigging.create_follicle2(self.surface,
                                                          u=base_u_val,
                                                          v=base_v_val)
        fol_clamped_v = fol_clamped_v_shape.getParent()
        fol_clamped_v.rename(fol_clamped_v_name)
        fol_clamped_v.setParent(self.grp_rig)

        fol_clamped_u_name = nomenclature_rig.resolve('influenceClampedU')
        fol_clamped_u_shape = libRigging.create_follicle2(self.surface,
                                                          u=base_u_val,
                                                          v=base_v_val)
        fol_clamped_u = fol_clamped_u_shape.getParent()
        fol_clamped_u.rename(fol_clamped_u_name)
        fol_clamped_u.setParent(self.grp_rig)

        # Clamp the values so they never fully reach 0 or 1 for U and V.
        util_clamp_uv = libRigging.create_utility_node(
            'clamp',
            inputR=attr_u_inn,
            inputG=attr_v_inn,
            minR=oob_step_size,
            minG=oob_step_size,
            maxR=1.0 - oob_step_size,
            maxG=1.0 - oob_step_size)
        clamped_u = util_clamp_uv.outputR
        clamped_v = util_clamp_uv.outputG

        pymel.connectAttr(clamped_v, fol_clamped_v.parameterV)
        pymel.connectAttr(attr_u_inn, fol_clamped_v.parameterU)

        pymel.connectAttr(attr_v_inn, fol_clamped_u.parameterV)
        pymel.connectAttr(clamped_u, fol_clamped_u.parameterU)

        # Compute the direction to add for U and V if we are out-of-bound.
        dir_oob_u = libRigging.create_utility_node(
            'plusMinusAverage',
            operation=2,
            input3D=[fol_influence.translate,
                     fol_clamped_u.translate]).output3D
        dir_oob_v = libRigging.create_utility_node(
            'plusMinusAverage',
            operation=2,
            input3D=[fol_influence.translate,
                     fol_clamped_v.translate]).output3D

        # Compute the offset to add for U and V

        condition_oob_u_neg = libRigging.create_utility_node(
            'condition',
            operation=4,  # less than
            firstTerm=attr_u_inn,
            secondTerm=0.0,
            colorIfTrueR=1.0,
            colorIfFalseR=0.0,
        ).outColorR
        condition_oob_u_pos = libRigging.create_utility_node(
            'condition',  # greater than
            operation=2,
            firstTerm=attr_u_inn,
            secondTerm=1.0,
            colorIfTrueR=1.0,
            colorIfFalseR=0.0,
        ).outColorR
        condition_oob_v_neg = libRigging.create_utility_node(
            'condition',
            operation=4,  # less than
            firstTerm=attr_v_inn,
            secondTerm=0.0,
            colorIfTrueR=1.0,
            colorIfFalseR=0.0,
        ).outColorR
        condition_oob_v_pos = libRigging.create_utility_node(
            'condition',  # greater than
            operation=2,
            firstTerm=attr_v_inn,
            secondTerm=1.0,
            colorIfTrueR=1.0,
            colorIfFalseR=0.0,
        ).outColorR

        # Compute the amount of oob
        oob_val_u_pos = libRigging.create_utility_node(
            'plusMinusAverage', operation=2, input1D=[attr_u_inn,
                                                      1.0]).output1D
        oob_val_u_neg = libRigging.create_utility_node('multiplyDivide',
                                                       input1X=attr_u_inn,
                                                       input2X=-1.0).outputX
        oob_val_v_pos = libRigging.create_utility_node(
            'plusMinusAverage', operation=2, input1D=[attr_v_inn,
                                                      1.0]).output1D
        oob_val_v_neg = libRigging.create_utility_node('multiplyDivide',
                                                       input1X=attr_v_inn,
                                                       input2X=-1.0).outputX
        oob_val_u = libRigging.create_utility_node(
            'condition',
            operation=0,
            firstTerm=condition_oob_u_pos,
            secondTerm=1.0,
            colorIfTrueR=oob_val_u_pos,
            colorIfFalseR=oob_val_u_neg).outColorR
        oob_val_v = libRigging.create_utility_node(
            'condition',
            operation=0,
            firstTerm=condition_oob_v_pos,
            secondTerm=1.0,
            colorIfTrueR=oob_val_v_pos,
            colorIfFalseR=oob_val_v_neg).outColorR

        oob_amount_u = libRigging.create_utility_node(
            'multiplyDivide',
            operation=2,
            input1X=oob_val_u,
            input2X=oob_step_size).outputX
        oob_amount_v = libRigging.create_utility_node(
            'multiplyDivide',
            operation=2,
            input1X=oob_val_v,
            input2X=oob_step_size).outputX

        oob_offset_u = libRigging.create_utility_node('multiplyDivide',
                                                      input1X=oob_amount_u,
                                                      input1Y=oob_amount_u,
                                                      input1Z=oob_amount_u,
                                                      input2=dir_oob_u).output
        oob_offset_v = libRigging.create_utility_node('multiplyDivide',
                                                      input1X=oob_amount_v,
                                                      input1Y=oob_amount_v,
                                                      input1Z=oob_amount_v,
                                                      input2=dir_oob_v).output

        # Add the U out-of-bound-offset only if the U is between 0.0 and 1.0
        oob_u_condition_1 = condition_oob_u_neg
        oob_u_condition_2 = condition_oob_u_pos
        oob_u_condition_added = libRigging.create_utility_node(
            'addDoubleLinear',
            input1=oob_u_condition_1,
            input2=oob_u_condition_2).output
        oob_u_condition_out = libRigging.create_utility_node(
            'condition',
            operation=0,  # equal
            firstTerm=oob_u_condition_added,
            secondTerm=1.0,
            colorIfTrue=oob_offset_u,
            colorIfFalse=[0, 0, 0]).outColor

        # Add the V out-of-bound-offset only if the V is between 0.0 and 1.0
        oob_v_condition_1 = condition_oob_v_neg
        oob_v_condition_2 = condition_oob_v_pos
        oob_v_condition_added = libRigging.create_utility_node(
            'addDoubleLinear',
            input1=oob_v_condition_1,
            input2=oob_v_condition_2).output
        oob_v_condition_out = libRigging.create_utility_node(
            'condition',
            operation=0,  # equal
            firstTerm=oob_v_condition_added,
            secondTerm=1.0,
            colorIfTrue=oob_offset_v,
            colorIfFalse=[0, 0, 0]).outColor

        oob_offset = libRigging.create_utility_node(
            'plusMinusAverage',
            input3D=[oob_u_condition_out, oob_v_condition_out]).output3D

        layer_oob = stack.append_layer('oobLayer')
        pymel.connectAttr(oob_offset, layer_oob.t)

        #
        # Create the third layer that apply the translation provided by the fb Avar.
        #

        layer_fb = stack.append_layer('fbLayer')
        attr_get_fb = libRigging.create_utility_node(
            'multiplyDivide',
            input1X=self.attr_fb,
            input2X=self._attr_length_u).outputX
        attr_get_fb_adjusted = libRigging.create_utility_node(
            'multiplyDivide', input1X=attr_get_fb, input2X=0.1).outputX
        pymel.connectAttr(attr_get_fb_adjusted, layer_fb.translateZ)

        #
        # Create the 4th layer (folRot) that apply the rotation provided by the follicle controlled by the ud and lr Avar.
        # This is necessary since we don't want to rotation to affect the oobLayer and fbLayer.
        #
        layer_follicle_rot = stack.append_layer('folRot')
        pymel.connectAttr(util_decomposeTM.outputRotate,
                          layer_follicle_rot.rotate)

        #
        # Create a 5th layer that apply the yw, pt, rl and Avar.
        #
        layer_rot = stack.append_layer('rotLayer')
        pymel.connectAttr(self.attr_yw, layer_rot.rotateY)
        pymel.connectAttr(self.attr_pt, layer_rot.rotateX)
        pymel.connectAttr(self.attr_rl, layer_rot.rotateZ)

        return stack