Beispiel #1
0
    def addOperators(self):
        """Create operators and set the relations for the component rig

        Apply operators, constraints, expressions to the hierarchy.
        In order to keep the code clean and easier to debug,
        we shouldn't create any new object in this method.

        """
        # Tangent position ---------------------------------
        # common part
        d = vector.getDistance(self.guide.pos["root"], self.guide.pos["neck"])
        dist_node = node.createDistNode(self.root, self.ik_ctl)
        rootWorld_node = node.createDecomposeMatrixNode(
            self.root.attr("worldMatrix"))
        div_node = node.createDivNode(dist_node + ".distance",
                                      rootWorld_node + ".outputScaleX")
        div_node = node.createDivNode(div_node + ".outputX", d)

        # tan0
        mul_node = node.createMulNode(self.tan0_att,
                                      self.tan0_loc.getAttr("ty"))
        res_node = node.createMulNode(mul_node + ".outputX",
                                      div_node + ".outputX")
        pm.connectAttr(res_node + ".outputX", self.tan0_loc + ".ty")

        # tan1
        mul_node = node.createMulNode(self.tan1_att,
                                      self.tan1_loc.getAttr("ty"))
        res_node = node.createMulNode(mul_node + ".outputX",
                                      div_node + ".outputX")
        pm.connectAttr(res_node + ".outputX", self.tan1_loc.attr("ty"))

        # Curves -------------------------------------------
        op = applyop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5,
                                         .5, .5)
        pm.connectAttr(self.maxstretch_att, op + ".maxstretch")
        pm.connectAttr(self.maxsquash_att, op + ".maxsquash")
        pm.connectAttr(self.softness_att, op + ".softness")

        # Volume driver ------------------------------------
        crv_node = node.createCurveInfoNode(self.slv_crv)

        # Division -----------------------------------------
        for i in range(self.settings["division"]):

            # References
            u = i / (self.settings["division"] - 1.0)

            cns = applyop.pathCns(self.div_cns[i], self.slv_crv, False, u,
                                  True)
            cns.setAttr("frontAxis", 1)  # front axis is 'Y'
            cns.setAttr("upAxis", 2)  # front axis is 'Z'

            # Roll
            intMatrix = applyop.gear_intmatrix_op(
                self.intMRef + ".worldMatrix", self.ik_ctl + ".worldMatrix", u)
            dm_node = node.createDecomposeMatrixNode(intMatrix + ".output")
            pm.connectAttr(dm_node + ".outputRotate",
                           self.twister[i].attr("rotate"))

            pm.parentConstraint(self.twister[i],
                                self.ref_twist[i],
                                maintainOffset=True)

            pm.connectAttr(self.ref_twist[i] + ".translate",
                           cns + ".worldUpVector")

            # Squash n Stretch
            op = applyop.gear_squashstretch2_op(self.fk_npo[i], self.root,
                                                pm.arclen(self.slv_crv), "y")

            pm.connectAttr(self.volume_att, op + ".blend")
            pm.connectAttr(crv_node + ".arcLength", op + ".driver")
            pm.connectAttr(self.st_att[i], op + ".stretch")
            pm.connectAttr(self.sq_att[i], op + ".squash")
            op.setAttr("driver_min", .1)

            # scl compas
            if i != 0:
                div_node = node.createDivNode([1, 1, 1], [
                    self.fk_npo[i - 1] + ".sx", self.fk_npo[i - 1] + ".sy",
                    self.fk_npo[i - 1] + ".sz"
                ])

                pm.connectAttr(div_node + ".output",
                               self.scl_npo[i] + ".scale")

            # Controlers
            if i == 0:
                mulmat_node = applyop.gear_mulmatrix_op(
                    self.div_cns[i].attr("worldMatrix"),
                    self.root.attr("worldInverseMatrix"))
            else:
                mulmat_node = applyop.gear_mulmatrix_op(
                    self.div_cns[i].attr("worldMatrix"),
                    self.div_cns[i - 1].attr("worldInverseMatrix"))

            dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output")
            pm.connectAttr(dm_node + ".outputTranslate",
                           self.fk_npo[i].attr("t"))
            pm.connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r"))

            # Orientation Lock
            if i == self.settings["division"] - 1:
                dm_node = node.createDecomposeMatrixNode(self.ik_ctl +
                                                         ".worldMatrix")
                blend_node = node.createBlendNode(
                    [dm_node + ".outputRotate%s" % s for s in "XYZ"],
                    [cns + ".rotate%s" % s for s in "XYZ"], self.lock_ori_att)
                self.div_cns[i].attr("rotate").disconnect()

                pm.connectAttr(blend_node + ".output",
                               self.div_cns[i] + ".rotate")

        # Head ---------------------------------------------
        self.fk_ctl[-1].addChild(self.head_cns)

        # scale compensation
        dm_node = node.createDecomposeMatrixNode(self.scl_npo[0] +
                                                 ".parentInverseMatrix")

        pm.connectAttr(dm_node + ".outputScale", self.scl_npo[0] + ".scale")
Beispiel #2
0
    def addOperators(self):
        """Create operators and set the relations for the component rig

        Apply operators, constraints, expressions to the hierarchy.
        In order to keep the code clean and easier to debug,
        we shouldn't create any new object in this method.

        """

        # Auto bend ----------------------------
        if self.settings["autoBend"]:
            mul_node = node.createMulNode(
                [self.autoBendChain[0].ry, self.autoBendChain[0].rz],
                [self.sideBend_att, self.frontBend_att])

            mul_node.outputX >> self.ik1autoRot_lvl.rz
            mul_node.outputY >> self.ik1autoRot_lvl.rx

            self.ikHandleAutoBend = primitive.addIkHandle(
                self.autoBend_ctl,
                self.getName("ikHandleAutoBend"),
                self.autoBendChain, "ikSCsolver")

        # Tangent position ---------------------------------
        # common part
        d = vector.getDistance(self.guide.apos[0], self.guide.apos[1])
        dist_node = node.createDistNode(self.ik0_ctl, self.ik1_ctl)

        rootWorld_node = node.createDecomposeMatrixNode(
            self.root.attr("worldMatrix"))

        div_node = node.createDivNode(dist_node + ".distance",
                                      rootWorld_node + ".outputScaleX")

        div_node = node.createDivNode(div_node + ".outputX", d)

        # tan0
        mul_node = node.createMulNode(self.tan0_att,
                                      self.tan0_npo.getAttr("ty"))

        res_node = node.createMulNode(mul_node + ".outputX",
                                      div_node + ".outputX")

        pm.connectAttr(res_node + ".outputX",
                       self.tan0_npo.attr("ty"))

        # tan1
        mul_node = node.createMulNode(self.tan1_att,
                                      self.tan1_npo.getAttr("ty"))

        res_node = node.createMulNode(mul_node + ".outputX",
                                      div_node + ".outputX")

        pm.connectAttr(res_node + ".outputX", self.tan1_npo.attr("ty"))

        # Tangent Mid --------------------------------------
        if self.settings["centralTangent"]:
            tanIntMat = applyop.gear_intmatrix_op(
                self.tan0_npo.attr("worldMatrix"),
                self.tan1_npo.attr("worldMatrix"),
                .5)

            applyop.gear_mulmatrix_op(
                tanIntMat.attr("output"),
                self.tan_npo.attr("parentInverseMatrix[0]"),
                self.tan_npo)

            pm.connectAttr(self.tan_ctl.attr("translate"),
                           self.tan0_off.attr("translate"))

            pm.connectAttr(self.tan_ctl.attr("translate"),
                           self.tan1_off.attr("translate"))

        # Curves -------------------------------------------
        op = applyop.gear_curveslide2_op(
            self.slv_crv, self.mst_crv, 0, 1.5, .5, .5)

        pm.connectAttr(self.position_att, op + ".position")
        pm.connectAttr(self.maxstretch_att, op + ".maxstretch")
        pm.connectAttr(self.maxsquash_att, op + ".maxsquash")
        pm.connectAttr(self.softness_att, op + ".softness")

        # Volume driver ------------------------------------
        crv_node = node.createCurveInfoNode(self.slv_crv)

        # Division -----------------------------------------
        for i in range(self.settings["division"]):

            # References
            u = i / (self.settings["division"] - 1.0)

            cns = applyop.pathCns(
                self.div_cns[i], self.slv_crv, False, u, True)
            cns.setAttr("frontAxis", 1)  # front axis is 'Y'
            cns.setAttr("upAxis", 0)  # front axis is 'X'

            # Roll
            intMatrix = applyop.gear_intmatrix_op(
                self.ik0_ctl + ".worldMatrix",
                self.ik1_ctl + ".worldMatrix",
                u)

            dm_node = node.createDecomposeMatrixNode(intMatrix + ".output")
            pm.connectAttr(dm_node + ".outputRotate",
                           self.twister[i].attr("rotate"))

            pm.parentConstraint(self.twister[i],
                                self.ref_twist[i],
                                maintainOffset=True)

            pm.connectAttr(self.ref_twist[i] + ".translate",
                           cns + ".worldUpVector")

            # compensate scale reference
            div_node = node.createDivNode([1, 1, 1],
                                          [rootWorld_node + ".outputScaleX",
                                           rootWorld_node + ".outputScaleY",
                                           rootWorld_node + ".outputScaleZ"])

            # Squash n Stretch
            op = applyop.gear_squashstretch2_op(
                self.scl_transforms[i],
                self.root,
                pm.arclen(self.slv_crv),
                "y",
                div_node + ".output")

            pm.connectAttr(self.volume_att, op + ".blend")
            pm.connectAttr(crv_node + ".arcLength", op + ".driver")
            pm.connectAttr(self.st_att[i], op + ".stretch")
            pm.connectAttr(self.sq_att[i], op + ".squash")

            # Controlers
            if i == 0:
                mulmat_node = applyop.gear_mulmatrix_op(
                    self.div_cns[i].attr("worldMatrix"),
                    self.root.attr("worldInverseMatrix"))

                dm_node = node.createDecomposeMatrixNode(
                    mulmat_node + ".output")

                pm.connectAttr(dm_node + ".outputTranslate",
                               self.fk_npo[i].attr("t"))

            else:
                mulmat_node = applyop.gear_mulmatrix_op(
                    self.div_cns[i].attr("worldMatrix"),
                    self.div_cns[i - 1].attr("worldInverseMatrix"))

                dm_node = node.createDecomposeMatrixNode(
                    mulmat_node + ".output")

                mul_node = node.createMulNode(div_node + ".output",
                                              dm_node + ".outputTranslate")

                pm.connectAttr(mul_node + ".output",
                               self.fk_npo[i].attr("t"))

            pm.connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r"))

            # Orientation Lock
            if i == 0:
                dm_node = node.createDecomposeMatrixNode(
                    self.ik0_ctl + ".worldMatrix")

                blend_node = node.createBlendNode(
                    [dm_node + ".outputRotate%s" % s for s in "XYZ"],
                    [cns + ".rotate%s" % s for s in "XYZ"],
                    self.lock_ori0_att)

                self.div_cns[i].attr("rotate").disconnect()
                pm.connectAttr(blend_node + ".output",
                               self.div_cns[i] + ".rotate")

            elif i == self.settings["division"] - 1:
                dm_node = node.createDecomposeMatrixNode(
                    self.ik1_ctl + ".worldMatrix")

                blend_node = node.createBlendNode(
                    [dm_node + ".outputRotate%s" % s for s in "XYZ"],
                    [cns + ".rotate%s" % s for s in "XYZ"],
                    self.lock_ori1_att)

                self.div_cns[i].attr("rotate").disconnect()
                pm.connectAttr(blend_node + ".output",
                               self.div_cns[i] + ".rotate")

        # Connections (Hooks) ------------------------------

        pm.parentConstraint(self.scl_transforms[0], self.cnx0)
        pm.scaleConstraint(self.scl_transforms[0], self.cnx0)
        pm.parentConstraint(self.scl_transforms[-1], self.cnx1)
        pm.scaleConstraint(self.scl_transforms[-1], self.cnx1)
Beispiel #3
0
def eyeRig(eyeMesh,
           edgeLoop,
           blinkH,
           namePrefix,
           offset,
           rigidLoops,
           falloffLoops,
           headJnt,
           doSkin,
           parent=None,
           ctlName="ctl",
           sideRange=False,
           customCorner=False,
           intCorner=None,
           extCorner=None,
           ctlGrp=None,
           defGrp=None):

    """Create eyelid and eye rig

    Args:
        eyeMesh (TYPE): Description
        edgeLoop (TYPE): Description
        blinkH (TYPE): Description
        namePrefix (TYPE): Description
        offset (TYPE): Description
        rigidLoops (TYPE): Description
        falloffLoops (TYPE): Description
        headJnt (TYPE): Description
        doSkin (TYPE): Description
        parent (None, optional): Description
        ctlName (str, optional): Description
        sideRange (bool, optional): Description
        customCorner (bool, optional): Description
        intCorner (None, optional): Description
        extCorner (None, optional): Description
        ctlGrp (None, optional): Description
        defGrp (None, optional): Description

    Returns:
        TYPE: Description
    """
    # Checkers
    if edgeLoop:
        edgeLoopList = [pm.PyNode(e) for e in edgeLoop.split(",")]
    else:
        pm.displayWarning("Please set the edge loop first")
        return

    if eyeMesh:
        try:
            eyeMesh = pm.PyNode(eyeMesh)
        except pm.MayaNodeError:
            pm.displayWarning("The object %s can not be found in the "
                              "scene" % (eyeMesh))
            return
    else:
        pm.displayWarning("Please set the eye mesh first")

    if doSkin:
        if not headJnt:
            pm.displayWarning("Please set the Head Jnt or unCheck "
                              "Compute Topological Autoskin")
            return

    # Initial Data
    bboxCenter = meshNavigation.bboxCenter(eyeMesh)

    extr_v = meshNavigation.getExtremeVertexFromLoop(edgeLoopList, sideRange)
    upPos = extr_v[0]
    lowPos = extr_v[1]
    inPos = extr_v[2]
    outPos = extr_v[3]
    edgeList = extr_v[4]
    vertexList = extr_v[5]

    # Detect the side L or R from the x value
    if inPos.getPosition(space='world')[0] < 0.0:
        side = "R"
        inPos = extr_v[3]
        outPos = extr_v[2]
        normalPos = outPos
        npw = normalPos.getPosition(space='world')
        normalVec = npw - bboxCenter
    else:
        side = "L"
        normalPos = outPos
        npw = normalPos.getPosition(space='world')
        normalVec = bboxCenter - npw
    # Manual Vertex corners
    if customCorner:
        if intCorner:
            try:
                if side == "R":
                    inPos = pm.PyNode(extCorner)
                else:
                    inPos = pm.PyNode(intCorner)
            except pm.MayaNodeError:
                pm.displayWarning("%s can not be found" % intCorner)
                return
        else:
            pm.displayWarning("Please set the internal eyelid corner")
            return

        if extCorner:
            try:
                normalPos = pm.PyNode(extCorner)
                npw = normalPos.getPosition(space='world')
                if side == "R":
                    outPos = pm.PyNode(intCorner)
                    normalVec = npw - bboxCenter
                else:
                    outPos = pm.PyNode(extCorner)
                    normalVec = bboxCenter - npw
            except pm.MayaNodeError:
                pm.displayWarning("%s can not be found" % extCorner)
                return
        else:
            pm.displayWarning("Please set the external eyelid corner")
            return

    # Check if we have prefix:
    if namePrefix:
        namePrefix = string.removeInvalidCharacter(namePrefix)
    else:
        pm.displayWarning("Prefix is needed")
        return

    def setName(name, ind=None):
        namesList = [namePrefix, side, name]
        if ind is not None:
            namesList[1] = side + str(ind)
        name = "_".join(namesList)
        return name

    if pm.ls(setName("root")):
        pm.displayWarning("The object %s already exist in the scene. Please "
                          "choose another name prefix" % setName("root"))
        return

    # Eye root
    eye_root = primitive.addTransform(None, setName("root"))
    eyeCrv_root = primitive.addTransform(eye_root, setName("crvs"))

    # Eyelid Main crvs
    try:
        upEyelid = meshNavigation.edgeRangeInLoopFromMid(
            edgeList, upPos, inPos, outPos)
        upCrv = curve.createCurveFromOrderedEdges(
            upEyelid, inPos, setName("upperEyelid"), parent=eyeCrv_root)
        upCrv_ctl = curve.createCurveFromOrderedEdges(
            upEyelid, inPos, setName("upCrv_%s" % ctlName), parent=eyeCrv_root)
        pm.rebuildCurve(upCrv_ctl, s=2, rt=0, rpo=True, ch=False)

        lowEyelid = meshNavigation.edgeRangeInLoopFromMid(
            edgeList, lowPos, inPos, outPos)
        lowCrv = curve.createCurveFromOrderedEdges(
            lowEyelid, inPos, setName("lowerEyelid"), parent=eyeCrv_root)
        lowCrv_ctl = curve.createCurveFromOrderedEdges(
            lowEyelid,
            inPos,
            setName("lowCrv_%s" % ctlName),
            parent=eyeCrv_root)

        pm.rebuildCurve(lowCrv_ctl, s=2, rt=0, rpo=True, ch=False)

    except UnboundLocalError:
        if customCorner:
            pm.displayWarning("This error is maybe caused because the custom "
                              "Corner vertex is not part of the edge loop")
        pm.displayError(traceback.format_exc())
        return

    upBlink = curve.createCurveFromCurve(
        upCrv, setName("upblink_crv"), nbPoints=30, parent=eyeCrv_root)
    lowBlink = curve.createCurveFromCurve(
        lowCrv, setName("lowBlink_crv"), nbPoints=30, parent=eyeCrv_root)

    upTarget = curve.createCurveFromCurve(
        upCrv, setName("upblink_target"), nbPoints=30, parent=eyeCrv_root)
    lowTarget = curve.createCurveFromCurve(
        lowCrv, setName("lowBlink_target"), nbPoints=30, parent=eyeCrv_root)
    midTarget = curve.createCurveFromCurve(
        lowCrv, setName("midBlink_target"), nbPoints=30, parent=eyeCrv_root)

    rigCrvs = [upCrv,
               lowCrv,
               upCrv_ctl,
               lowCrv_ctl,
               upBlink,
               lowBlink,
               upTarget,
               lowTarget,
               midTarget]

    for crv in rigCrvs:
        crv.attr("visibility").set(False)

    # localBBOX
    localBBox = eyeMesh.getBoundingBox(invisible=True, space='world')
    wRadius = abs((localBBox[0][0] - localBBox[1][0]))
    dRadius = abs((localBBox[0][1] - localBBox[1][1]) / 1.7)

    # Groups
    if not ctlGrp:
        ctlGrp = "rig_controllers_grp"
    try:
        ctlSet = pm.PyNode(ctlGrp)
    except pm.MayaNodeError:
        pm.sets(n=ctlGrp, em=True)
        ctlSet = pm.PyNode(ctlGrp)
    if not defGrp:
        defGrp = "rig_deformers_grp"
    try:
        defset = pm.PyNode(defGrp)
    except pm.MayaNodeError:
        pm.sets(n=defGrp, em=True)
        defset = pm.PyNode(defGrp)

    # Calculate center looking at
    averagePosition = ((upPos.getPosition(space='world')
                        + lowPos.getPosition(space='world')
                        + inPos.getPosition(space='world')
                        + outPos.getPosition(space='world'))
                       / 4)
    if side == "R":
        negate = False
        offset = offset
        over_offset = dRadius
    else:
        negate = False
        over_offset = dRadius

    if side == "R" and sideRange or side == "R" and customCorner:
        axis = "z-x"
        # axis = "zx"
    else:
        axis = "z-x"

    t = transform.getTransformLookingAt(
        bboxCenter,
        averagePosition,
        normalVec,
        axis=axis,
        negate=negate)

    over_npo = primitive.addTransform(
        eye_root, setName("center_lookatRoot"), t)

    over_ctl = icon.create(over_npo,
                           setName("over_%s" % ctlName),
                           t,
                           icon="square",
                           w=wRadius,
                           d=dRadius,
                           ro=datatypes.Vector(1.57079633, 0, 0),
                           po=datatypes.Vector(0, 0, over_offset),
                           color=4)
    node.add_controller_tag(over_ctl)
    attribute.add_mirror_config_channels(over_ctl)
    attribute.setKeyableAttributes(
        over_ctl,
        params=["tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz"])

    if side == "R":
        over_npo.attr("rx").set(over_npo.attr("rx").get() * -1)
        over_npo.attr("ry").set(over_npo.attr("ry").get() + 180)
        over_npo.attr("sz").set(-1)

    if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost":
        pass
    else:
        pm.sets(ctlSet, add=over_ctl)

    center_lookat = primitive.addTransform(
        over_ctl, setName("center_lookat"), t)

    # Tracking
    # Eye aim control
    t_arrow = transform.getTransformLookingAt(bboxCenter,
                                              averagePosition,
                                              upPos.getPosition(space='world'),
                                              axis="zy", negate=False)

    radius = abs((localBBox[0][0] - localBBox[1][0]) / 1.7)
    arrow_npo = primitive.addTransform(eye_root, setName("aim_npo"), t_arrow)
    arrow_ctl = icon.create(arrow_npo,
                            setName("aim_%s" % ctlName),
                            t_arrow,
                            icon="arrow",
                            w=1,
                            po=datatypes.Vector(0, 0, radius),
                            color=4)
    if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost":
        pass
    else:
        pm.sets(ctlSet, add=arrow_ctl)
    attribute.setKeyableAttributes(arrow_ctl, params=["rx", "ry", "rz"])

    # tracking custom trigger
    if side == "R":
        tt = t_arrow
    else:
        tt = t
    aimTrigger_root = primitive.addTransform(
        center_lookat, setName("aimTrigger_root"), tt)
    aimTrigger_lvl = primitive.addTransform(
        aimTrigger_root, setName("aimTrigger_lvl"), tt)
    aimTrigger_lvl.attr("tz").set(1.0)
    aimTrigger_ref = primitive.addTransform(
        aimTrigger_lvl, setName("aimTrigger_ref"), tt)
    aimTrigger_ref.attr("tz").set(0.0)
    # connect  trigger with arrow_ctl
    pm.parentConstraint(arrow_ctl, aimTrigger_ref, mo=True)

    # Controls lists
    upControls = []
    trackLvl = []

    # upper eyelid controls
    upperCtlNames = ["inCorner", "upInMid", "upMid", "upOutMid", "outCorner"]
    cvs = upCrv_ctl.getCVs(space="world")
    if side == "R" and not sideRange:
        # if side == "R":
        cvs = [cv for cv in reversed(cvs)]
    for i, cv in enumerate(cvs):
        if utils.is_odd(i):
            color = 14
            wd = .5
            icon_shape = "circle"
            params = ["tx", "ty", "tz"]
        else:
            color = 4
            wd = .7
            icon_shape = "square"
            params = ["tx",
                      "ty",
                      "tz",
                      "ro",
                      "rx",
                      "ry",
                      "rz",
                      "sx",
                      "sy",
                      "sz"]

        t = transform.setMatrixPosition(t, cvs[i])
        npo = primitive.addTransform(center_lookat,
                                     setName("%s_npo" % upperCtlNames[i]),
                                     t)
        npoBase = npo
        if i == 2:
            # we add an extra level to input the tracking ofset values
            npo = primitive.addTransform(npo,
                                         setName("%s_trk" % upperCtlNames[i]),
                                         t)
            trackLvl.append(npo)

        ctl = icon.create(npo,
                          setName("%s_%s" % (upperCtlNames[i], ctlName)),
                          t,
                          icon=icon_shape,
                          w=wd,
                          d=wd,
                          ro=datatypes.Vector(1.57079633, 0, 0),
                          po=datatypes.Vector(0, 0, offset),
                          color=color)
        attribute.add_mirror_config_channels(ctl)
        node.add_controller_tag(ctl, over_ctl)
        upControls.append(ctl)
        if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost":
            pass
        else:
            pm.sets(ctlSet, add=ctl)
        attribute.setKeyableAttributes(ctl, params)
        if side == "R":
            npoBase.attr("ry").set(180)
            npoBase.attr("sz").set(-1)

    # adding parent average contrains to odd controls
    for i, ctl in enumerate(upControls):
        if utils.is_odd(i):
            pm.parentConstraint(upControls[i - 1],
                                upControls[i + 1],
                                ctl.getParent(),
                                mo=True)

    # lower eyelid controls
    lowControls = [upControls[0]]
    lowerCtlNames = ["inCorner",
                     "lowInMid",
                     "lowMid",
                     "lowOutMid",
                     "outCorner"]

    cvs = lowCrv_ctl.getCVs(space="world")
    if side == "R" and not sideRange:
        cvs = [cv for cv in reversed(cvs)]
    for i, cv in enumerate(cvs):
        # we skip the first and last point since is already in the uper eyelid
        if i in [0, 4]:
            continue
        if utils.is_odd(i):
            color = 14
            wd = .5
            icon_shape = "circle"
            params = ["tx", "ty", "tz"]
        else:
            color = 4
            wd = .7
            icon_shape = "square"
            params = ["tx",
                      "ty",
                      "tz",
                      "ro",
                      "rx",
                      "ry",
                      "rz",
                      "sx",
                      "sy",
                      "sz"]

        t = transform.setMatrixPosition(t, cvs[i])
        npo = primitive.addTransform(center_lookat,
                                     setName("%s_npo" % lowerCtlNames[i]),
                                     t)
        npoBase = npo
        if i == 2:
            # we add an extra level to input the tracking ofset values
            npo = primitive.addTransform(npo,
                                         setName("%s_trk" % lowerCtlNames[i]),
                                         t)
            trackLvl.append(npo)
        ctl = icon.create(npo,
                          setName("%s_%s" % (lowerCtlNames[i], ctlName)),
                          t,
                          icon=icon_shape,
                          w=wd,
                          d=wd,
                          ro=datatypes.Vector(1.57079633, 0, 0),
                          po=datatypes.Vector(0, 0, offset),
                          color=color)
        attribute.add_mirror_config_channels(ctl)

        lowControls.append(ctl)
        if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost":
            pass
        else:
            pm.sets(ctlSet, add=ctl)
        attribute.setKeyableAttributes(ctl, params)
        # mirror behaviout on R side controls
        if side == "R":
            npoBase.attr("ry").set(180)
            npoBase.attr("sz").set(-1)
    for lctl in reversed(lowControls[1:]):
        node.add_controller_tag(lctl, over_ctl)
    lowControls.append(upControls[-1])

    # adding parent average contrains to odd controls
    for i, ctl in enumerate(lowControls):
        if utils.is_odd(i):
            pm.parentConstraint(lowControls[i - 1],
                                lowControls[i + 1],
                                ctl.getParent(),
                                mo=True)

    # Connecting control crvs with controls
    applyop.gear_curvecns_op(upCrv_ctl, upControls)
    applyop.gear_curvecns_op(lowCrv_ctl, lowControls)

    # adding wires
    w1 = pm.wire(upCrv, w=upBlink)[0]
    w2 = pm.wire(lowCrv, w=lowBlink)[0]

    w3 = pm.wire(upTarget, w=upCrv_ctl)[0]
    w4 = pm.wire(lowTarget, w=lowCrv_ctl)[0]

    # adding blendshapes
    bs_upBlink = pm.blendShape(upTarget,
                               midTarget,
                               upBlink,
                               n="blendShapeUpBlink")
    bs_lowBlink = pm.blendShape(lowTarget,
                                midTarget,
                                lowBlink,
                                n="blendShapeLowBlink")
    bs_mid = pm.blendShape(lowTarget,
                           upTarget,
                           midTarget,
                           n="blendShapeLowBlink")

    # setting blendshape reverse connections
    rev_node = pm.createNode("reverse")
    pm.connectAttr(bs_upBlink[0].attr(midTarget.name()), rev_node + ".inputX")
    pm.connectAttr(rev_node + ".outputX", bs_upBlink[0].attr(upTarget.name()))
    rev_node = pm.createNode("reverse")
    pm.connectAttr(bs_lowBlink[0].attr(midTarget.name()), rev_node + ".inputX")
    pm.connectAttr(rev_node + ".outputX",
                   bs_lowBlink[0].attr(lowTarget.name()))
    rev_node = pm.createNode("reverse")
    pm.connectAttr(bs_mid[0].attr(upTarget.name()), rev_node + ".inputX")
    pm.connectAttr(rev_node + ".outputX", bs_mid[0].attr(lowTarget.name()))

    # setting default values
    bs_mid[0].attr(upTarget.name()).set(blinkH)

    # joints root
    jnt_root = primitive.addTransformFromPos(
        eye_root, setName("joints"), pos=bboxCenter)

    # head joint
    if headJnt:
        try:
            headJnt = pm.PyNode(headJnt)
            jnt_base = headJnt
        except pm.MayaNodeError:
            pm.displayWarning(
                "Aborted can not find %s " % headJnt)
            return
    else:
        # Eye root
        jnt_base = jnt_root

    eyeTargets_root = primitive.addTransform(eye_root,
                                             setName("targets"))

    eyeCenter_jnt = rigbits.addJnt(arrow_ctl,
                                   jnt_base,
                                   grp=defset,
                                   jntName=setName("center_jnt"))

    # Upper Eyelid joints ##################################################

    cvs = upCrv.getCVs(space="world")
    upCrv_info = node.createCurveInfoNode(upCrv)

    # aim constrain targets and joints
    upperEyelid_aimTargets = []
    upperEyelid_jnt = []
    upperEyelid_jntRoot = []

    for i, cv in enumerate(cvs):

        # aim targets
        trn = primitive.addTransformFromPos(eyeTargets_root,
                                            setName("upEyelid_aimTarget", i),
                                            pos=cv)
        upperEyelid_aimTargets.append(trn)
        # connecting positions with crv
        pm.connectAttr(upCrv_info + ".controlPoints[%s]" % str(i),
                       trn.attr("translate"))

        # joints
        jntRoot = primitive.addJointFromPos(jnt_root,
                                            setName("upEyelid_jnt_base", i),
                                            pos=bboxCenter)
        jntRoot.attr("radius").set(.08)
        jntRoot.attr("visibility").set(False)
        upperEyelid_jntRoot.append(jntRoot)
        applyop.aimCns(jntRoot, trn, axis="zy", wupObject=jnt_root)

        jnt_ref = primitive.addJointFromPos(jntRoot,
                                            setName("upEyelid_jnt_ref", i),
                                            pos=cv)
        jnt_ref.attr("radius").set(.08)
        jnt_ref.attr("visibility").set(False)

        jnt = rigbits.addJnt(jnt_ref,
                             jnt_base,
                             grp=defset,
                             jntName=setName("upEyelid_jnt", i))
        upperEyelid_jnt.append(jnt)

    # Lower Eyelid joints ##################################################

    cvs = lowCrv.getCVs(space="world")
    lowCrv_info = node.createCurveInfoNode(lowCrv)

    # aim constrain targets and joints
    lowerEyelid_aimTargets = []
    lowerEyelid_jnt = []
    lowerEyelid_jntRoot = []

    for i, cv in enumerate(cvs):
        if i in [0, len(cvs) - 1]:
            continue

        # aim targets
        trn = primitive.addTransformFromPos(eyeTargets_root,
                                            setName("lowEyelid_aimTarget", i),
                                            pos=cv)
        lowerEyelid_aimTargets.append(trn)
        # connecting positions with crv
        pm.connectAttr(lowCrv_info + ".controlPoints[%s]" % str(i),
                       trn.attr("translate"))

        # joints
        jntRoot = primitive.addJointFromPos(jnt_root,
                                            setName("lowEyelid_base", i),
                                            pos=bboxCenter)
        jntRoot.attr("radius").set(.08)
        jntRoot.attr("visibility").set(False)
        lowerEyelid_jntRoot.append(jntRoot)
        applyop.aimCns(jntRoot, trn, axis="zy", wupObject=jnt_root)

        jnt_ref = primitive.addJointFromPos(jntRoot,
                                            setName("lowEyelid_jnt_ref", i),
                                            pos=cv)
        jnt_ref.attr("radius").set(.08)
        jnt_ref.attr("visibility").set(False)

        jnt = rigbits.addJnt(jnt_ref,
                             jnt_base,
                             grp=defset,
                             jntName=setName("lowEyelid_jnt", i))
        lowerEyelid_jnt.append(jnt)

    # Channels
    # Adding and connecting attributes for the blink
    up_ctl = upControls[2]
    blink_att = attribute.addAttribute(
        over_ctl, "blink", "float", 0, minValue=0, maxValue=1)
    blinkMult_att = attribute.addAttribute(
        over_ctl, "blinkMult", "float", 1, minValue=1, maxValue=2)
    midBlinkH_att = attribute.addAttribute(
        over_ctl, "blinkHeight", "float", blinkH, minValue=0, maxValue=1)
    mult_node = node.createMulNode(blink_att, blinkMult_att)
    pm.connectAttr(mult_node + ".outputX",
                   bs_upBlink[0].attr(midTarget.name()))
    pm.connectAttr(mult_node + ".outputX",
                   bs_lowBlink[0].attr(midTarget.name()))
    pm.connectAttr(midBlinkH_att, bs_mid[0].attr(upTarget.name()))

    low_ctl = lowControls[2]

    # Adding channels for eye tracking
    upVTracking_att = attribute.addAttribute(up_ctl,
                                             "vTracking",
                                             "float",
                                             .02,
                                             minValue=0,
                                             maxValue=1,
                                             keyable=False,
                                             channelBox=True)
    upHTracking_att = attribute.addAttribute(up_ctl,
                                             "hTracking",
                                             "float",
                                             .01,
                                             minValue=0,
                                             maxValue=1,
                                             keyable=False,
                                             channelBox=True)

    lowVTracking_att = attribute.addAttribute(low_ctl,
                                              "vTracking",
                                              "float",
                                              .01,
                                              minValue=0,
                                              maxValue=1,
                                              keyable=False,
                                              channelBox=True)
    lowHTracking_att = attribute.addAttribute(low_ctl,
                                              "hTracking",
                                              "float",
                                              .01,
                                              minValue=0,
                                              maxValue=1,
                                              keyable=False,
                                              channelBox=True)

    mult_node = node.createMulNode(upVTracking_att, aimTrigger_ref.attr("ty"))
    pm.connectAttr(mult_node + ".outputX", trackLvl[0].attr("ty"))
    mult_node = node.createMulNode(upHTracking_att, aimTrigger_ref.attr("tx"))
    pm.connectAttr(mult_node + ".outputX", trackLvl[0].attr("tx"))

    mult_node = node.createMulNode(lowVTracking_att, aimTrigger_ref.attr("ty"))
    pm.connectAttr(mult_node + ".outputX", trackLvl[1].attr("ty"))
    mult_node = node.createMulNode(lowHTracking_att, aimTrigger_ref.attr("tx"))
    pm.connectAttr(mult_node + ".outputX", trackLvl[1].attr("tx"))

    # Tension on blink
    node.createReverseNode(blink_att, w1.scale[0])
    node.createReverseNode(blink_att, w3.scale[0])
    node.createReverseNode(blink_att, w2.scale[0])
    node.createReverseNode(blink_att, w4.scale[0])

    ###########################################
    # Reparenting
    ###########################################
    if parent:
        try:
            if isinstance(parent, basestring):
                parent = pm.PyNode(parent)
            parent.addChild(eye_root)
        except pm.MayaNodeError:
            pm.displayWarning("The eye rig can not be parent to: %s. Maybe "
                              "this object doesn't exist." % parent)

    ###########################################
    # Auto Skinning
    ###########################################
    if doSkin:
        # eyelid vertex rows
        totalLoops = rigidLoops + falloffLoops
        vertexLoopList = meshNavigation.getConcentricVertexLoop(vertexList,
                                                                totalLoops)
        vertexRowList = meshNavigation.getVertexRowsFromLoops(vertexLoopList)

        # we set the first value 100% for the first initial loop
        skinPercList = [1.0]
        # we expect to have a regular grid topology
        for r in range(rigidLoops):
            for rr in range(2):
                skinPercList.append(1.0)
        increment = 1.0 / float(falloffLoops)
        # we invert to smooth out from 100 to 0
        inv = 1.0 - increment
        for r in range(falloffLoops):
            for rr in range(2):
                if inv < 0.0:
                    inv = 0.0
                skinPercList.append(inv)
            inv -= increment

        # this loop add an extra 0.0 indices to avoid errors
        for r in range(10):
            for rr in range(2):
                skinPercList.append(0.0)

        # base skin
        geo = pm.listRelatives(edgeLoopList[0], parent=True)[0]
        # Check if the object has a skinCluster
        objName = pm.listRelatives(geo, parent=True)[0]

        skinCluster = skin.getSkinCluster(objName)
        if not skinCluster:
            skinCluster = pm.skinCluster(headJnt,
                                         geo,
                                         tsb=True,
                                         nw=2,
                                         n='skinClsEyelid')

        eyelidJoints = upperEyelid_jnt + lowerEyelid_jnt
        pm.progressWindow(title='Auto skinning process',
                          progress=0,
                          max=len(eyelidJoints))
        firstBoundary = False
        for jnt in eyelidJoints:
            pm.progressWindow(e=True, step=1, status='\nSkinning %s' % jnt)
            skinCluster.addInfluence(jnt, weight=0)
            v = meshNavigation.getClosestVertexFromTransform(geo, jnt)

            for row in vertexRowList:

                if v in row:
                    it = 0  # iterator
                    inc = 1  # increment
                    for i, rv in enumerate(row):
                        try:
                            perc = skinPercList[it]
                            t_val = [(jnt, perc), (headJnt, 1.0 - perc)]
                            pm.skinPercent(skinCluster,
                                           rv,
                                           transformValue=t_val)
                            if rv.isOnBoundary():
                                # we need to compare with the first boundary
                                # to check if the row have inverted direction
                                # and offset the value
                                if not firstBoundary:
                                    firstBoundary = True
                                    firstBoundaryValue = it

                                else:
                                    if it < firstBoundaryValue:
                                        it -= 1
                                    elif it > firstBoundaryValue:
                                        it += 1
                                inc = 2
                        except IndexError:
                            continue

                        it = it + inc
        pm.progressWindow(e=True, endProgress=True)

        # Eye Mesh skinning
        skinCluster = skin.getSkinCluster(eyeMesh)
        if not skinCluster:
            skinCluster = pm.skinCluster(eyeCenter_jnt,
                                         eyeMesh,
                                         tsb=True,
                                         nw=1,
                                         n='skinClsEye')
Beispiel #4
0
    def addOperators(self):

        # Tangent position ---------------------------------
        # common part
        d = vec.getDistance(self.guide.pos["root"], self.guide.pos["neck"])
        dist_node = nod.createDistNode(self.root, self.ik_ctl)
        rootWorld_node = nod.createDecomposeMatrixNode(
            self.root.attr("worldMatrix"))
        div_node = nod.createDivNode(dist_node + ".distance",
                                     rootWorld_node + ".outputScaleX")
        div_node = nod.createDivNode(div_node + ".outputX", d)

        # tan0
        mul_node = nod.createMulNode(self.tan0_att,
                                     self.tan0_loc.getAttr("ty"))
        res_node = nod.createMulNode(mul_node + ".outputX",
                                     div_node + ".outputX")
        connectAttr(res_node + ".outputX", self.tan0_loc + ".ty")

        # tan1
        mul_node = nod.createMulNode(self.tan1_att,
                                     self.tan1_loc.getAttr("ty"))
        res_node = nod.createMulNode(mul_node + ".outputX",
                                     div_node + ".outputX")
        connectAttr(res_node + ".outputX", self.tan1_loc.attr("ty"))

        # Curves -------------------------------------------
        op = aop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5, .5,
                                     .5)
        connectAttr(self.maxstretch_att, op + ".maxstretch")
        connectAttr(self.maxsquash_att, op + ".maxsquash")
        connectAttr(self.softness_att, op + ".softness")

        # Volume driver ------------------------------------
        crv_node = nod.createCurveInfoNode(self.slv_crv)

        # Division -----------------------------------------
        for i in range(self.settings["division"]):

            # References
            u = i / (self.settings["division"] - 1.0)

            cns = aop.pathCns(self.div_cns[i], self.slv_crv, False, u, True)
            cns.setAttr("frontAxis", 1)  # front axis is 'Y'
            cns.setAttr("upAxis", 2)  # front axis is 'Z'

            # Roll
            aop.gear_spinePointAtOp(cns, self.root, self.ik_ctl, u, "Z")

            # Squash n Stretch
            op = aop.gear_squashstretch2_op(self.fk_npo[i], self.root,
                                            arclen(self.slv_crv), "y")
            connectAttr(self.volume_att, op + ".blend")
            connectAttr(crv_node + ".arcLength", op + ".driver")
            connectAttr(self.st_att[i], op + ".stretch")
            connectAttr(self.sq_att[i], op + ".squash")

            # scl compas
            if i != 0:
                div_node = nod.createDivNode([1, 1, 1], [
                    self.fk_npo[i - 1] + ".sx", self.fk_npo[i - 1] + ".sy",
                    self.fk_npo[i - 1] + ".sz"
                ])
                connectAttr(div_node + ".output", self.scl_npo[i] + ".scale")

            # Controlers
            if i == 0:
                mulmat_node = aop.gear_mulmatrix_op(
                    self.div_cns[i].attr("worldMatrix"),
                    self.root.attr("worldInverseMatrix"))
            else:
                mulmat_node = aop.gear_mulmatrix_op(
                    self.div_cns[i].attr("worldMatrix"),
                    self.div_cns[i - 1].attr("worldInverseMatrix"))

            dm_node = nod.createDecomposeMatrixNode(mulmat_node + ".output")
            connectAttr(dm_node + ".outputTranslate", self.fk_npo[i].attr("t"))
            connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r"))
            #connectAttr(dm_node+".outputScale", self.fk_npo[i].attr("s"))

            # Orientation Lock
            if i == self.settings["division"] - 1:
                dm_node = nod.createDecomposeMatrixNode(self.ik_ctl +
                                                        ".worldMatrix")
                blend_node = nod.createBlendNode(
                    [dm_node + ".outputRotate%s" % s for s in "XYZ"],
                    [cns + ".rotate%s" % s for s in "XYZ"], self.lock_ori_att)
                self.div_cns[i].attr("rotate").disconnect()
                connectAttr(blend_node + ".output",
                            self.div_cns[i] + ".rotate")

        # Head ---------------------------------------------
        self.fk_ctl[-1].addChild(self.head_cns)
Beispiel #5
0
    def addOperators(self):

        # Tangent position ---------------------------------
        # common part
        d = vec.getDistance(self.guide.apos[0], self.guide.apos[1])
        dist_node = nod.createDistNode(self.ik0_ctl, self.ik1_ctl)
        rootWorld_node = nod.createDecomposeMatrixNode(self.root.attr("worldMatrix"))
        div_node = nod.createDivNode(dist_node+".distance", rootWorld_node+".outputScaleX")
        div_node = nod.createDivNode(div_node+".outputX", d)

        # tan0
        mul_node = nod.createMulNode(self.tan0_att, self.tan0_npo.getAttr("ty"))
        res_node = nod.createMulNode(mul_node+".outputX", div_node+".outputX")
        pm.connectAttr( res_node+".outputX", self.tan0_npo.attr("ty"))

        # tan1
        mul_node = nod.createMulNode(self.tan1_att, self.tan1_npo.getAttr("ty"))
        res_node = nod.createMulNode(mul_node+".outputX", div_node+".outputX")
        pm.connectAttr( res_node+".outputX", self.tan1_npo.attr("ty"))

        # Curves -------------------------------------------
        op = aop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5, .5, .5)

        pm.connectAttr(self.position_att, op+".position")
        pm.connectAttr(self.maxstretch_att, op+".maxstretch")
        pm.connectAttr(self.maxsquash_att, op+".maxsquash")
        pm.connectAttr(self.softness_att, op+".softness")

        # Volume driver ------------------------------------
        crv_node = nod.createCurveInfoNode(self.slv_crv)

        # Division -----------------------------------------
        for i in range(self.settings["division"]):

            # References
            u = i / (self.settings["division"] - 1.0)

            cns = aop.pathCns(self.div_cns[i], self.slv_crv, False, u, True)
            cns.setAttr("frontAxis", 1)# front axis is 'Y'
            cns.setAttr("upAxis", 0)# front axis is 'X'

            # Roll
            intMatrix = aop.gear_intmatrix_op(self.ik0_ctl+".worldMatrix", self.ik1_ctl+".worldMatrix", u)
            dm_node = nod.createDecomposeMatrixNode(intMatrix+".output")
            pm.connectAttr(dm_node+".outputRotate", self.twister[i].attr("rotate"))


            pm.parentConstraint(self.twister[i], self.ref_twist[i], maintainOffset=True)


            pm.connectAttr(self.ref_twist[i]+".translate", cns+".worldUpVector")

            # Squash n Stretch
            op = aop.gear_squashstretch2_op(self.fk_npo[i], self.root, pm.arclen(self.slv_crv), "y")
            pm.connectAttr(self.volume_att, op+".blend")
            pm.connectAttr(crv_node+".arcLength", op+".driver")
            pm.connectAttr(self.st_att[i], op+".stretch")
            pm.connectAttr(self.sq_att[i], op+".squash")

            # scl compensation

            if i == 0:
                dm_node = nod.createDecomposeMatrixNode(self.root+".worldMatrix")
                div_node = nod.createDivNode([1,1,1], [dm_node+".outputScaleX", dm_node+".outputScaleY", dm_node+".outputScaleZ"])
                pm.connectAttr(div_node+".output", self.scl_npo[i]+".scale")

            elif i == 1:
                div_node = nod.createDivNode([1,1,1], [self.fk_npo[i-1]+".sx", self.fk_npo[i-1]+".sy", self.fk_npo[i-1]+".sz"])
                pm.connectAttr(div_node+".output", self.scl_npo[i]+".scale")


            else:
                div_node = nod.createDivNode([1,1,1], [self.fk_npo[i-1]+".sx", self.fk_npo[i-1]+".sy", self.fk_npo[i-1]+".sz"])
                pm.connectAttr(div_node+".output", self.scl_npo[i]+".scale")


            # Controlers
            if i == 0:
                mulmat_node = aop.gear_mulmatrix_op(self.div_cns[i].attr("worldMatrix"),
                                                    self.scl_npo[0].attr("worldInverseMatrix"))
            else:
                mulmat_node = aop.gear_mulmatrix_op(self.div_cns[i].attr("worldMatrix"),
                                                    self.div_cns[i - 1].attr("worldInverseMatrix"))
            dm_node = nod.createDecomposeMatrixNode(mulmat_node+".output")
            pm.connectAttr(dm_node+".outputTranslate", self.fk_npo[i].attr("t"))
            pm.connectAttr(dm_node+".outputRotate", self.fk_npo[i].attr("r"))

            # Orientation Lock
            if i == 0 :
                dm_node = nod.createDecomposeMatrixNode(self.ik0_ctl+".worldMatrix")
                blend_node = nod.createBlendNode([dm_node+".outputRotate%s"%s for s in "XYZ"], [cns+".rotate%s"%s for s in "XYZ"], self.lock_ori0_att)
                self.div_cns[i].attr("rotate").disconnect()
                pm.connectAttr(blend_node+".output", self.div_cns[i]+".rotate")
            elif i == self.settings["division"] - 1 :
                dm_node = nod.createDecomposeMatrixNode(self.ik1_ctl+".worldMatrix")
                blend_node = nod.createBlendNode([dm_node+".outputRotate%s"%s for s in "XYZ"], [cns+".rotate%s"%s for s in "XYZ"], self.lock_ori1_att)
                self.div_cns[i].attr("rotate").disconnect()
                pm.connectAttr(blend_node+".output", self.div_cns[i]+".rotate")

        # Connections (Hooks) ------------------------------
        pm.pointConstraint(self.div_cns[0], self.cnx0)
        pm.orientConstraint(self.div_cns[0], self.cnx0)
        pm.pointConstraint(self.fk_ctl[-1], self.cnx1)
        pm.orientConstraint(self.fk_ctl[-1], self.cnx1)
Beispiel #6
0
    def addOperators(self):

        # Tangent position ---------------------------------
        # common part
        d = vec.getDistance(self.guide.pos["root"], self.guide.pos["neck"])
        dist_node = nod.createDistNode(self.root, self.ik_ctl)
        rootWorld_node = nod.createDecomposeMatrixNode(self.root.attr("worldMatrix"))
        div_node = nod.createDivNode(dist_node+".distance", rootWorld_node+".outputScaleX")
        div_node = nod.createDivNode(div_node+".outputX", d)

        # tan0
        mul_node = nod.createMulNode(self.tan0_att, self.tan0_loc.getAttr("ty"))
        res_node = nod.createMulNode(mul_node+".outputX", div_node+".outputX")
        connectAttr( res_node+".outputX", self.tan0_loc+".ty")

        # tan1
        mul_node = nod.createMulNode(self.tan1_att, self.tan1_loc.getAttr("ty"))
        res_node = nod.createMulNode(mul_node+".outputX", div_node+".outputX")
        connectAttr( res_node+".outputX", self.tan1_loc.attr("ty"))

        # Curves -------------------------------------------
        op = aop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5, .5, .5)
        connectAttr(self.maxstretch_att, op+".maxstretch")
        connectAttr(self.maxsquash_att, op+".maxsquash")
        connectAttr(self.softness_att, op+".softness")

        # Volume driver ------------------------------------
        crv_node = nod.createCurveInfoNode(self.slv_crv)

        # Division -----------------------------------------
        for i in range(self.settings["division"]):

            # References
            u = i / (self.settings["division"] - 1.0)

            cns = aop.pathCns(self.div_cns[i], self.slv_crv, False, u, True)
            cns.setAttr("frontAxis", 1)# front axis is 'Y'
            cns.setAttr("upAxis", 2)# front axis is 'Z'

            # Roll
            aop.gear_spinePointAtOp(cns, self.root, self.ik_ctl, u, "Z")

            # Squash n Stretch
            op = aop.gear_squashstretch2_op(self.fk_npo[i], self.root, arclen(self.slv_crv), "y")
            connectAttr(self.volume_att, op+".blend")
            connectAttr(crv_node+".arcLength", op+".driver")
            connectAttr(self.st_att[i], op+".stretch")
            connectAttr(self.sq_att[i], op+".squash")

            # scl compas
            if i != 0:
                div_node = nod.createDivNode([1,1,1], [self.fk_npo[i-1]+".sx", self.fk_npo[i-1]+".sy", self.fk_npo[i-1]+".sz"])
                connectAttr(div_node+".output", self.scl_npo[i]+".scale")

            # Controlers
            if i == 0:
                mulmat_node = aop.gear_mulmatrix_op(self.div_cns[i].attr("worldMatrix"), self.root.attr("worldInverseMatrix"))
            else:
                mulmat_node = aop.gear_mulmatrix_op(self.div_cns[i].attr("worldMatrix"), self.div_cns[i-1].attr("worldInverseMatrix"))

            dm_node = nod.createDecomposeMatrixNode(mulmat_node+".output")
            connectAttr(dm_node+".outputTranslate", self.fk_npo[i].attr("t"))
            connectAttr(dm_node+".outputRotate", self.fk_npo[i].attr("r"))
            #connectAttr(dm_node+".outputScale", self.fk_npo[i].attr("s"))

            # Orientation Lock
            if i == self.settings["division"] - 1 :
                dm_node = nod.createDecomposeMatrixNode(self.ik_ctl+".worldMatrix")
                blend_node = nod.createBlendNode([dm_node+".outputRotate%s"%s for s in "XYZ"], [cns+".rotate%s"%s for s in "XYZ"], self.lock_ori_att)
                self.div_cns[i].attr("rotate").disconnect()
                connectAttr(blend_node+".output", self.div_cns[i]+".rotate")


        # Head ---------------------------------------------
        self.fk_ctl[-1].addChild(self.head_cns)