def build(self, no_subdiv=False, num_ctrl=None, degree=3, create_ctrl=True, constraint=False, rot_fol=True, *args,
              **kwargs):
        super(Ribbon, self).build(create_grp_anm=create_ctrl, *args, **kwargs)
        if num_ctrl is not None:
            self.num_ctrl = num_ctrl

        nomenclature_rig = self.get_nomenclature_rig()

        # Create the plane and align it with the selected bones
        plane_tran = next(
            (input for input in self.input if libPymel.isinstance_of_shape(input, pymel.nodetypes.NurbsSurface)), None)
        if plane_tran is None:
            plane_name = nomenclature_rig.resolve("ribbonPlane")
            if no_subdiv:  # We don't want any subdivision in the plane, so use only 2 bones to create it
                no_subdiv_degree = 2
                if degree < 2:
                    no_subdiv_degree = degree
                plane_tran = libRigging.create_nurbs_plane_from_joints([self.chain_jnt[0], self.chain_jnt[-1]],
                                                                       degree=no_subdiv_degree, width=self.width)
            else:
                plane_tran = libRigging.create_nurbs_plane_from_joints(self.chain_jnt, degree=degree, width=self.width)
            plane_tran.rename(plane_name)
            plane_tran.setParent(self.grp_rig)
        self._ribbon_shape = plane_tran.getShape()

        # Create the follicule needed for the system on the skinned bones
        self.attach_to_plane(rot_fol)
        # TODO : Support aim constraint for bones instead of follicle rotation?

        follicles_grp = pymel.createNode("transform")
        follicle_grp_name = nomenclature_rig.resolve("follicleGrp")
        follicles_grp.rename(follicle_grp_name)
        follicles_grp.setParent(self.grp_rig)
        for n in self._follicles:
            n.setParent(follicles_grp)

        # Create the joints that will drive the ribbon.
        # TODO: Support other shapes than straight lines...
        self._ribbon_jnts = libRigging.create_chain_between_objects(
            self.chain_jnt.start, self.chain_jnt.end, self.num_ctrl, parented=False)

        # Group all the joints
        ribbon_chain_grp_name = nomenclature_rig.resolve('ribbonChainGrp')
        self.ribbon_chain_grp = pymel.createNode('transform', name=ribbon_chain_grp_name, parent=self.grp_rig)
        align_chain = True if len(self.chain_jnt) == len(self._ribbon_jnts) else False
        for i, jnt in enumerate(self._ribbon_jnts):
            # Align the ribbon joints with the real joint to have a better rotation ctrl
            ribbon_jnt_name = nomenclature_rig.resolve('ribbonJnt{0:02d}'.format(i))
            jnt.rename(ribbon_jnt_name)
            jnt.setParent(self.ribbon_chain_grp)
            if align_chain:
                matrix = self.chain_jnt[i].getMatrix(worldSpace=True)
                jnt.setMatrix(matrix, worldSpace=True)

        # TODO - Improve skinning smoothing by setting manually the skin...
        pymel.skinCluster(list(self._ribbon_jnts), plane_tran, dr=1.0, mi=2.0, omi=True)
        try:
            libSkinning.assign_weights_from_segments(self._ribbon_shape, self._ribbon_jnts, dropoff=1.0)
        except ZeroDivisionError, e:
            pass
Beispiel #2
0
    def build(self, no_subdiv=False, num_ctrl = None, degree=3, create_ctrl=True, constraint=False, rot_fol=True, *args, **kwargs):
        super(Ribbon, self).build(create_grp_anm=create_ctrl, *args, **kwargs)
        if num_ctrl is not None:
            self.num_ctrl = num_ctrl

        nomenclature_rig = self.get_nomenclature_rig()

        # Create the plane and align it with the selected bones
        plane_tran = next((input for input in self.input if libPymel.isinstance_of_shape(input, pymel.nodetypes.NurbsSurface)), None)
        if plane_tran is None:
            plane_name = nomenclature_rig.resolve("ribbonPlane")
            if no_subdiv:  # We don't want any subdivision in the plane, so use only 2 bones to create it
                no_subdiv_degree = 2
                if degree < 2:
                    no_subdiv_degree = degree
                plane_tran = libRigging.create_nurbs_plane_from_joints([self.chain_jnt[0], self.chain_jnt[-1]],
                                                                       degree=no_subdiv_degree, width=self.width)
            else:
                plane_tran = libRigging.create_nurbs_plane_from_joints(self.chain_jnt, degree=degree, width=self.width)
            plane_tran.rename(plane_name)
            plane_tran.setParent(self.grp_rig)
        self._ribbon_shape = plane_tran.getShape()

        # Create the follicule needed for the system on the skinned bones
        self.attach_to_plane(rot_fol)
        # TODO : Support aim constraint for bones instead of follicle rotation?

        follicles_grp = pymel.createNode("transform")
        follicle_grp_name = nomenclature_rig.resolve("follicleGrp")
        follicles_grp.rename(follicle_grp_name)
        follicles_grp.setParent(self.grp_rig)
        for n in self._follicles:
            n.setParent(follicles_grp)

        # Create the joints that will drive the ribbon.
        # TODO: Support other shapes than straight lines...
        self._ribbon_jnts = libRigging.create_chain_between_objects(
            self.chain_jnt.start, self.chain_jnt.end, self.num_ctrl, parented=False)

        # Group all the joints
        ribbon_chain_grp_name = nomenclature_rig.resolve('ribbonChainGrp')
        self.ribbon_chain_grp = pymel.createNode('transform', name=ribbon_chain_grp_name, parent=self.grp_rig)
        align_chain = True if len(self.chain_jnt) == len(self._ribbon_jnts) else False
        for i, jnt in enumerate(self._ribbon_jnts):
            # Align the ribbon joints with the real joint to have a better rotation ctrl
            ribbon_jnt_name = nomenclature_rig.resolve('ribbonJnt{0:02d}'.format(i))
            jnt.rename(ribbon_jnt_name)
            jnt.setParent(self.ribbon_chain_grp)
            if align_chain:
                matrix = self.chain_jnt[i].getMatrix(worldSpace=True)
                jnt.setMatrix(matrix, worldSpace=True)

        # TODO - Improve skinning smoothing by setting manually the skin...
        pymel.skinCluster(list(self._ribbon_jnts), plane_tran, dr=1.0, mi=2.0, omi=True)
        try:
            libSkinning.assign_weights_from_segments(self._ribbon_shape, self._ribbon_jnts, dropoff=1.0)
        except ZeroDivisionError, e:
            pass
Beispiel #3
0
    def build(self, rig, num_subdiv = 5, num_ctrl = None, degree=1, create_ctrl=True, constraint=False, rot_fol=False, *args, **kwargs):
        super(Ribbon, self).build(rig, create_grp_anm=create_ctrl, *args, **kwargs)
        if num_ctrl is not None:
            self.num_ctrl = num_ctrl

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

        #Create the plane and align it with the selected bones
        plane_tran = next((input for input in self.input if libPymel.isinstance_of_shape(input, pymel.nodetypes.NurbsSurface)), None)
        if plane_tran is None:
            plane_name = nomenclature_rig.resolve("ribbonPlane")
            plane_tran = libRigging.create_nurbs_plane_from_joints(self.chain_jnt, degree=degree, width=self.width)
            plane_tran.rename(plane_name)
            plane_tran.setParent(self.grp_rig)
        self._ribbon_shape = plane_tran.getShape()

        # TODO: Remove usage of djRivet
        #Create the follicule needed for the system on the skinned bones
        for i, jnt in enumerate(self.chain_jnt):
            pymel.select(jnt, plane_tran)
            mel.eval("djRivet")
            #TODO : Support aim constraint for bones instead of follicle rotation?

        # Apply the skin on the plane and rename follicle from djRivet
        dj_rivet_grp = pymel.PyNode("djRivetX")
        follicle_grp_name = nomenclature_rig.resolve("follicle_grp")
        dj_rivet_grp.rename(follicle_grp_name)
        dj_rivet_grp.setParent(self.grp_rig)
        self._follicles = dj_rivet_grp.getChildren()
        for n in self._follicles:
            fol_name = nomenclature_rig.resolve("fol")
            n.rename(fol_name)

        # Create the joints that will drive the ribbon.
        # TODO: Support other shapes than straight lines...
        # TODO: Support ctrl hold/fetch when building/unbuilding.
        self._ribbon_jnts = libRigging.create_chain_between_objects(
            self.chain_jnt.start, self.chain_jnt.end, self.num_ctrl, parented=False)

        # Group all the joints
        ribbon_chain_grp_name = nomenclature_rig.resolve('ribbonChain' + "_grp")
        ribbon_chain_grp = pymel.createNode('transform', name=ribbon_chain_grp_name, parent=self.grp_rig)
        align_chain = True if len(self.chain_jnt) == len(self._ribbon_jnts) else False
        for i, jnt in enumerate(self._ribbon_jnts):
            #Align the ribbon joints with the real joint to have a better rotation ctrl
            if align_chain:
                matrix = self.chain_jnt[i].getMatrix(worldSpace=True)
                jnt.setMatrix(matrix, worldSpace=True)
            jnt.setParent(ribbon_chain_grp)

        #TODO - Improve skinning smoothing by setting manully the skin...
        pymel.skinCluster(list(self._ribbon_jnts), plane_tran, dr=1.0, mi=2.0, omi=True)
        try:
            libSkinning.assign_weights_from_segments(self._ribbon_shape, self._ribbon_jnts, dropoff=1.0)
        except ZeroDivisionError, e:
            pass
Beispiel #4
0
    def build(self,
              orient_ik_ctrl=True,
              num_twist=None,
              create_bend=None,
              realign=True,
              *args,
              **kwargs):
        if len(self.chain_jnt) < 2:
            raise Exception(
                "Invalid input count. Expected 2, got {0}. {1}".format(
                    len(self.chain_jnt), self.chain_jnt))

        # Support some properties that could be redefined at build time
        if num_twist:
            self.num_twist = num_twist

        if create_bend:
            self.create_bend = create_bend

        super(Twistbone, self).build(create_grp_anm=self.create_bend,
                                     *args,
                                     **kwargs)

        nomenclature_rig = self.get_nomenclature_rig()
        nomenclature_jnt = self.get_nomenclature_jnt()

        top_parent = self.chain[0].getParent()
        jnt_s = self.chain_jnt[0]
        jnt_e = self.chain_jnt[1]

        scalable_grp = pymel.createNode('transform')
        scalable_grp.setParent(self.grp_rig)
        scalable_grp.rename(nomenclature_rig.resolve('scalable'))

        # Handle case where the number of twist change. The skin will be lost
        if self.subjnts and len(self.subjnts) != self.num_twist:
            self.unassign_twist_weights()
            pymel.delete(self.subjnts)
            self.subjnts = []
            # Also invalidate ctrls
            self.ctrls = []
        # Generate Subjoints if necessary, they will be use as the skinned one and will be drived by the ribbon and
        # driver chain
        if not self.subjnts:
            self.subjnts = libRigging.create_chain_between_objects(
                jnt_s, jnt_e, self.num_twist)
        elif realign:
            # Position the subjnts at equidistance from each others.
            num_subjnts = len(self.subjnts)
            base_tm = jnt_s.getMatrix(worldSpace=True)
            sp = jnt_s.getTranslation(space='world')
            ep = jnt_e.getTranslation(space='world')
            delta = ep - sp
            for i, subjnt in enumerate(self.subjnts):
                ratio = float(i) / (num_subjnts - 1)
                tm = base_tm.copy()
                tm.translate = delta * ratio + sp
                subjnt.setMatrix(tm, worldSpace=True)

        self.subjnts[0].setParent(jnt_s)

        driver_grp = pymel.createNode('transform')
        driver_grp.setParent(scalable_grp)
        driver_grp.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True)
        driver_grp.rename(nomenclature_rig.resolve('driverJnt_grp'))
        pymel.parentConstraint(jnt_s, driver_grp)

        # Create a second chain that will drive the rotation of the skinned joint
        driverjnts = libRigging.create_chain_between_objects(
            jnt_s, jnt_e, self.num_twist)

        # Rename the skinning subjnts
        for i, sub_jnt in enumerate(self.subjnts):
            sub_jnt.segmentScaleCompensate.set(
                0)  # Remove segment scale compensate
            # Right now, we take into consideration that the system will be named Side_SysName(Ex:Upperarm_Twist)
            jnt_name = nomenclature_jnt.resolve("twist{0:02d}".format(i))
            sub_jnt.rename(jnt_name)

        driver_refs = []
        ctrl_refs = []  # Will be used to drive the ctrl when stretch append
        for i, driver_jnt in enumerate(driverjnts):
            driver_jnt.segmentScaleCompensate.set(
                0)  # Remove segment scale compensate
            driver_name = nomenclature_jnt.resolve(
                "twistDriver{0:02d}".format(i))
            driver_jnt.rename(driver_name)
            # Parent all driver joint to the driver group and ensure 0 rotation
            driver_jnt.setParent(driver_grp)
            driver_jnt.rotate.set(0, 0, 0)
            driver_jnt.jointOrient.set(0, 0, 0)
            # Create a transform that will drive the twist data
            driver_jnt_ref = pymel.createNode('transform')
            driver_jnt_ref.setParent(driver_jnt)
            driver_jnt_ref.setMatrix(driver_jnt.getMatrix(worldSpace=True),
                                     worldSpace=True)
            driver_jnt_ref.rename(driver_name + '_ref')
            driver_refs.append(
                driver_jnt_ref
            )  # Keep them to connect the ref on the subjnts later
            if self.create_bend:
                if i != 0 and i != (
                        len(driverjnts) - 1
                ):  # There will be no ctrl for the first and last twist jnt
                    ctrl_driver = pymel.createNode("transform")
                    ctrl_driver_name = nomenclature_jnt.resolve(
                        "ctrlDriver{0:02d}".format(i))
                    ctrl_driver.rename(ctrl_driver_name)
                    ctrl_driver.setParent(driver_grp)
                    ctrl_refs.append(ctrl_driver)

        if not self.create_bend:
            # Parent the two extremity of the ribbon to the twist driver associated
            pymel.pointConstraint(jnt_s, driverjnts[0], mo=False)
            pymel.pointConstraint(jnt_e, driverjnts[-1], mo=False)

        mid_idx = math.ceil((self.num_twist / 2.0))
        before_mid_idx = math.floor((self.num_twist / 2.0))
        if self.create_bend:
            # Create Ribbon
            sys_ribbon = Ribbon(self.subjnts,
                                name=nomenclature_rig.resolve("bendRibbon"),
                                rig=self.rig)
            sys_ribbon.build(create_ctrl=False,
                             degree=3,
                             num_ctrl=self.num_twist,
                             no_subdiv=False,
                             rot_fol=False)
            self.ctrls = sys_ribbon.create_ctrls(ctrls=self.ctrls,
                                                 no_extremity=True,
                                                 constraint_rot=False,
                                                 refs=self.chain_jnt[1])
            # Point constraint the driver jnt on the ribbon jnt to drive the bending
            for i, driver in enumerate(driverjnts):
                pymel.pointConstraint(sys_ribbon._ribbon_jnts[i],
                                      driver,
                                      mo=True)
                # Aim constraint the driver to create the bend effect. Skip the middle one if it as one
                # TODO - Find a best way to determine the side
                aim_vec = [
                    1.0, 0.0, 0.0
                ] if nomenclature_rig.side == nomenclature_rig.SIDE_L else [
                    -1.0, 0.0, 0.0
                ]
                aim_vec_inverse = [
                    -1.0, 0.0, 0.0
                ] if nomenclature_rig.side == nomenclature_rig.SIDE_L else [
                    1.0, 0.0, 0.0
                ]
                if mid_idx != before_mid_idx and i == (mid_idx - 1):
                    continue
                if i <= mid_idx - 1:
                    pymel.aimConstraint(sys_ribbon._follicles[i + 1],
                                        driver,
                                        mo=False,
                                        wut=2,
                                        wuo=jnt_s,
                                        aim=aim_vec,
                                        u=[0.0, 1.0, 0.0])
                else:
                    pymel.aimConstraint(sys_ribbon._follicles[i - 1],
                                        driver,
                                        mo=False,
                                        wut=2,
                                        wuo=jnt_s,
                                        aim=aim_vec_inverse,
                                        u=[0.0, 1.0, 0.0])
            for ctrl, ref in zip(self.ctrls, ctrl_refs):
                #libAttr.lock_hide_rotation(ctrl)
                libAttr.lock_hide_scale(ctrl)
                ctrl.setParent(self.grp_anm)
                pymel.parentConstraint(ref, ctrl.offset, mo=True)
            # We don't want the ribbon to scale with the system since it will follow with it's bone
            sys_ribbon.grp_rig.setParent(self.grp_rig)
            # Ensure that the ribbon jnts are following the start jnt correctly
            pymel.parentConstraint(jnt_s, sys_ribbon.ribbon_chain_grp, mo=True)
            # Parent the two extremity of the ribbon to the twist driver associated
            pymel.pointConstraint(jnt_s, sys_ribbon._ribbon_jnts[0], mo=False)
            pymel.pointConstraint(jnt_e, sys_ribbon._ribbon_jnts[-1], mo=False)

        # Create the first non roll system
        nonroll_sys_start = NonRollJoint()
        nonroll_sys_start.build(jnt_s,
                                name=nomenclature_rig.resolve("nonrollStart"))
        nonroll_sys_start.setMatrix(jnt_s.getMatrix(worldSpace=True),
                                    worldSpace=True)
        if top_parent:
            pymel.orientConstraint(top_parent,
                                   nonroll_sys_start.node,
                                   maintainOffset=True)
            pymel.pointConstraint(jnt_s, nonroll_sys_start.node)
        pymel.parentConstraint(jnt_s,
                               nonroll_sys_start.ikHandle,
                               maintainOffset=True)

        # Create the second non-roll system
        nonroll_sys_end = NonRollJoint()
        nonroll_sys_end.build(jnt_e,
                              name=nomenclature_rig.resolve("nonrollEnd"))
        nonroll_sys_end.setMatrix(jnt_e.getMatrix(worldSpace=True),
                                  worldSpace=True)
        # nonroll_sys_end.setTranslation(jnt_e.getTranslation(space='world'), space='world')
        pymel.orientConstraint(jnt_s,
                               nonroll_sys_end.node,
                               maintainOffset=True)
        pymel.pointConstraint(jnt_e, nonroll_sys_end.node)
        pymel.parentConstraint(jnt_e,
                               nonroll_sys_end.ikHandle,
                               maintainOffset=True)

        # Setup twist extraction using the two non-roll twist extractor
        # The start ref will be connected as inverted to prevent it to twist
        mdl_reverse_rotx_start = pymel.createNode("multDoubleLinear")
        pymel.connectAttr(nonroll_sys_start.twist_extractor.rotateX,
                          mdl_reverse_rotx_start.input1)
        mdl_reverse_rotx_start.input2.set(-1.0)
        pymel.connectAttr(mdl_reverse_rotx_start.output,
                          driver_refs[0].rotateX)

        # The last ref will be directly be connected
        pymel.connectAttr(nonroll_sys_end.twist_extractor.rotateX,
                          driver_refs[-1].rotateX)

        # Finally, compute the twist value for the middle twists joints, also connect the ctrl rotation in the system
        # for more control
        twist_split_len = len(driver_refs[1:-1])
        for i, ref in enumerate(driver_refs[1:-1]):
            blend_w_node = pymel.createNode("blendWeighted")
            pymel.connectAttr(mdl_reverse_rotx_start.output,
                              blend_w_node.input[0])
            pymel.connectAttr(nonroll_sys_end.twist_extractor.rotateX,
                              blend_w_node.input[1])
            # Compute the weight of each twist in the system
            weight_start = (1.0 /
                            (twist_split_len + 1)) * (twist_split_len - i)
            weight_end = 1.0 - weight_start
            blend_w_node.weight[0].set(weight_start)
            blend_w_node.weight[1].set(weight_end)
            blend_w_node.weight[2].set(1.0)
            pymel.connectAttr(blend_w_node.output, ref.rotateX)
            if self.create_bend:
                pymel.connectAttr(self.ctrls[i].rotateX, blend_w_node.input[2])
                pymel.connectAttr(self.ctrls[i].rotateY, ref.rotateY)
                pymel.connectAttr(self.ctrls[i].rotateZ, ref.rotateZ)

        # Cleanup
        nonroll_sys_start.setParent(scalable_grp)
        nonroll_sys_end.setParent(scalable_grp)

        # Compute the Stretch
        attr_stretch_raw = libRigging.create_stretch_node_between_2_bones(
            jnt_s, jnt_e, self.grp_rig.globalScale)
        pymel.connectAttr(attr_stretch_raw, driver_grp.scaleX)

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

        for driver_ref, jnt in zip(driver_refs, self.subjnts):
            # If we have the bend, just orient constraint cause the subjnt are constrained to the follicle of the ribbon
            if self.create_bend:
                pymel.orientConstraint(driver_ref, jnt, mo=False)
            else:
                pymel.parentConstraint(driver_ref, jnt, mo=False)
            '''
            pymel.connectAttr(driver_ref.scaleX, jnt.scaleX)
            pymel.connectAttr(driver_ref.scaleY, jnt.scaleY)
            pymel.connectAttr(driver_ref.scaleZ, jnt.scaleZ)
            '''

        if self.auto_skin:
            self.assign_twist_weights()
Beispiel #5
0
    def build(self, orient_ik_ctrl=True, num_twist=None, create_bend=None, realign=True, *args, **kwargs):
        if len(self.chain_jnt) < 2:
            raise Exception("Invalid input count. Expected 2, got {0}. {1}".format(len(self.chain_jnt), self.chain_jnt))

        # Support some properties that could be redefined at build time
        if num_twist:
            self.num_twist = num_twist

        if create_bend:
            self.create_bend = create_bend

        super(Twistbone, self).build(create_grp_anm=self.create_bend, *args, **kwargs)

        nomenclature_rig = self.get_nomenclature_rig()
        nomenclature_jnt = self.get_nomenclature_jnt()

        top_parent = self.chain[0].getParent()
        jnt_s = self.chain_jnt[0]
        jnt_e = self.chain_jnt[1]

        scalable_grp = pymel.createNode('transform')
        scalable_grp.setParent(self.grp_rig)
        scalable_grp.rename(nomenclature_rig.resolve('scalable'))

        # Handle case where the number of twist change. The skin will be lost
        if self.subjnts and len(self.subjnts) != self.num_twist:
            self.unassign_twist_weights()
            pymel.delete(self.subjnts)
            self.subjnts = []
            # Also invalidate ctrls
            self.ctrls = []
        # Generate Subjoints if necessary, they will be use as the skinned one and will be drived by the ribbon and
        # driver chain
        if not self.subjnts:
            self.subjnts = libRigging.create_chain_between_objects(jnt_s, jnt_e, self.num_twist)
        elif realign:
            # Position the subjnts at equidistance from each others.
            num_subjnts = len(self.subjnts)
            base_tm = jnt_s.getMatrix(worldSpace=True)
            sp = jnt_s.getTranslation(space='world')
            ep = jnt_e.getTranslation(space='world')
            delta = ep - sp
            for i, subjnt in enumerate(self.subjnts):
                ratio = float(i) / (num_subjnts-1)
                tm = base_tm.copy()
                tm.translate = delta * ratio + sp
                subjnt.setMatrix(tm, worldSpace=True)

        self.subjnts[0].setParent(jnt_s)

        driver_grp = pymel.createNode('transform')
        driver_grp.setParent(scalable_grp)
        driver_grp.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True)
        driver_grp.rename(nomenclature_rig.resolve('driverJnt_grp'))
        pymel.parentConstraint(jnt_s, driver_grp)

        # Create a second chain that will drive the rotation of the skinned joint
        driverjnts = libRigging.create_chain_between_objects(jnt_s, jnt_e, self.num_twist)

        # Rename the skinning subjnts
        for i, sub_jnt in enumerate(self.subjnts):
            sub_jnt.segmentScaleCompensate.set(0)  # Remove segment scale compensate
            # Right now, we take into consideration that the system will be named Side_SysName(Ex:Upperarm_Twist)
            jnt_name = nomenclature_jnt.resolve("twist{0:02d}".format(i))
            sub_jnt.rename(jnt_name)

        driver_refs = []
        ctrl_refs = []  # Will be used to drive the ctrl when stretch append
        for i, driver_jnt in enumerate(driverjnts):
            driver_jnt.segmentScaleCompensate.set(0)  # Remove segment scale compensate
            driver_name = nomenclature_jnt.resolve("twistDriver{0:02d}".format(i))
            driver_jnt.rename(driver_name)
            # Parent all driver joint to the driver group and ensure 0 rotation
            driver_jnt.setParent(driver_grp)
            driver_jnt.rotate.set(0, 0, 0)
            driver_jnt.jointOrient.set(0, 0, 0)
            # Create a transform that will drive the twist data
            driver_jnt_ref = pymel.createNode('transform')
            driver_jnt_ref.setParent(driver_jnt)
            driver_jnt_ref.setMatrix(driver_jnt.getMatrix(worldSpace=True), worldSpace=True)
            driver_jnt_ref.rename(driver_name + '_ref')
            driver_refs.append(driver_jnt_ref) # Keep them to connect the ref on the subjnts later
            if self.create_bend:
                if i != 0 and i != (len(driverjnts) - 1):  # There will be no ctrl for the first and last twist jnt
                    ctrl_driver = pymel.createNode("transform")
                    ctrl_driver_name = nomenclature_jnt.resolve("ctrlDriver{0:02d}".format(i))
                    ctrl_driver.rename(ctrl_driver_name)
                    ctrl_driver.setParent(driver_grp)
                    ctrl_refs.append(ctrl_driver)

        if not self.create_bend:
            # Parent the two extremity of the ribbon to the twist driver associated
            pymel.pointConstraint(jnt_s, driverjnts[0], mo=False)
            pymel.pointConstraint(jnt_e, driverjnts[-1], mo=False)

        mid_idx = math.ceil((self.num_twist/2.0))
        before_mid_idx = math.floor((self.num_twist/2.0))
        if self.create_bend:
            # Create Ribbon
            sys_ribbon = Ribbon(self.subjnts, name=nomenclature_rig.resolve("bendRibbon"), rig=self.rig)
            sys_ribbon.build(create_ctrl=False, degree=3, num_ctrl=self.num_twist, no_subdiv=False, rot_fol=False)
            self.ctrls = sys_ribbon.create_ctrls(ctrls=self.ctrls, no_extremity=True,
                                                 constraint_rot=False, refs=self.chain_jnt[1])
            # Point constraint the driver jnt on the ribbon jnt to drive the bending
            for i, driver in enumerate(driverjnts):
                pymel.pointConstraint(sys_ribbon._ribbon_jnts[i], driver, mo=True)
                # Aim constraint the driver to create the bend effect. Skip the middle one if it as one
                # TODO - Find a best way to determine the side
                aim_vec = [1.0, 0.0, 0.0] if nomenclature_rig.side == nomenclature_rig.SIDE_L else [-1.0, 0.0, 0.0]
                aim_vec_inverse = [-1.0, 0.0, 0.0] if nomenclature_rig.side == nomenclature_rig.SIDE_L else [1.0, 0.0, 0.0]
                if mid_idx != before_mid_idx and i == (mid_idx - 1):
                    continue
                if i <= mid_idx - 1:
                    pymel.aimConstraint(sys_ribbon._follicles[i + 1], driver,
                                        mo=False, wut=2, wuo=jnt_s, aim=aim_vec, u=[0.0, 1.0, 0.0])
                else:
                    pymel.aimConstraint(sys_ribbon._follicles[i - 1], driver,
                                        mo=False, wut=2, wuo=jnt_s, aim=aim_vec_inverse, u=[0.0, 1.0, 0.0])
            for ctrl, ref in zip(self.ctrls, ctrl_refs):
                #libAttr.lock_hide_rotation(ctrl)
                libAttr.lock_hide_scale(ctrl)
                ctrl.setParent(self.grp_anm)
                pymel.parentConstraint(ref, ctrl.offset, mo=True)
            # We don't want the ribbon to scale with the system since it will follow with it's bone
            sys_ribbon.grp_rig.setParent(self.grp_rig)
            # Ensure that the ribbon jnts are following the start jnt correctly
            pymel.parentConstraint(jnt_s, sys_ribbon.ribbon_chain_grp, mo=True)
            # Parent the two extremity of the ribbon to the twist driver associated
            pymel.pointConstraint(jnt_s, sys_ribbon._ribbon_jnts[0], mo=False)
            pymel.pointConstraint(jnt_e, sys_ribbon._ribbon_jnts[-1], mo=False)

        # Create the first non roll system
        nonroll_sys_start = NonRollJoint()
        nonroll_sys_start.build(jnt_s, name=nomenclature_rig.resolve("nonrollStart"))
        nonroll_sys_start.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True)
        if top_parent:
            pymel.orientConstraint(top_parent, nonroll_sys_start.node, maintainOffset=True)
            pymel.pointConstraint(jnt_s, nonroll_sys_start.node)
        pymel.parentConstraint(jnt_s, nonroll_sys_start.ikHandle, maintainOffset=True)

        # Create the second non-roll system
        nonroll_sys_end = NonRollJoint()
        nonroll_sys_end.build(jnt_e, name=nomenclature_rig.resolve("nonrollEnd"))
        nonroll_sys_end.setMatrix(jnt_e.getMatrix(worldSpace=True), worldSpace=True)
        # nonroll_sys_end.setTranslation(jnt_e.getTranslation(space='world'), space='world')
        pymel.orientConstraint(jnt_s, nonroll_sys_end.node, maintainOffset=True)
        pymel.pointConstraint(jnt_e, nonroll_sys_end.node)
        pymel.parentConstraint(jnt_e, nonroll_sys_end.ikHandle, maintainOffset=True)

        # Setup twist extraction using the two non-roll twist extractor
        # The start ref will be connected as inverted to prevent it to twist
        mdl_reverse_rotx_start = pymel.createNode("multDoubleLinear")
        pymel.connectAttr(nonroll_sys_start.twist_extractor.rotateX, mdl_reverse_rotx_start.input1)
        mdl_reverse_rotx_start.input2.set(-1.0)
        pymel.connectAttr(mdl_reverse_rotx_start.output, driver_refs[0].rotateX)

        # The last ref will be directly be connected
        pymel.connectAttr(nonroll_sys_end.twist_extractor.rotateX, driver_refs[-1].rotateX)

        # Finally, compute the twist value for the middle twists joints, also connect the ctrl rotation in the system
        # for more control
        twist_split_len = len(driver_refs[1:-1])
        for i, ref in enumerate(driver_refs[1:-1]):
            blend_w_node = pymel.createNode("blendWeighted")
            pymel.connectAttr(mdl_reverse_rotx_start.output, blend_w_node.input[0])
            pymel.connectAttr(nonroll_sys_end.twist_extractor.rotateX, blend_w_node.input[1])
            # Compute the weight of each twist in the system
            weight_start = (1.0 / (twist_split_len + 1)) * (twist_split_len - i)
            weight_end = 1.0 - weight_start
            blend_w_node.weight[0].set(weight_start)
            blend_w_node.weight[1].set(weight_end)
            blend_w_node.weight[2].set(1.0)
            pymel.connectAttr(blend_w_node.output, ref.rotateX)
            if self.create_bend:
                pymel.connectAttr(self.ctrls[i].rotateX, blend_w_node.input[2])
                pymel.connectAttr(self.ctrls[i].rotateY, ref.rotateY)
                pymel.connectAttr(self.ctrls[i].rotateZ, ref.rotateZ)

        # Cleanup
        nonroll_sys_start.setParent(scalable_grp)
        nonroll_sys_end.setParent(scalable_grp)

        # Compute the Stretch
        attr_stretch_raw = libRigging.create_stretch_node_between_2_bones(jnt_s, jnt_e, self.grp_rig.globalScale)
        pymel.connectAttr(attr_stretch_raw, driver_grp.scaleX)

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

        for driver_ref, jnt in zip(driver_refs, self.subjnts):
            # If we have the bend, just orient constraint cause the subjnt are constrained to the follicle of the ribbon
            if self.create_bend:
                pymel.orientConstraint(driver_ref, jnt, mo=False)
            else:
                pymel.parentConstraint(driver_ref, jnt, mo=False)
            '''
            pymel.connectAttr(driver_ref.scaleX, jnt.scaleX)
            pymel.connectAttr(driver_ref.scaleY, jnt.scaleY)
            pymel.connectAttr(driver_ref.scaleZ, jnt.scaleZ)
            '''

        if self.auto_skin:
            self.assign_twist_weights()
Beispiel #6
0
    def build(self, rig, orient_ik_ctrl=True, create_boxes=True, *args, **kwargs):
        if len(self.chain_jnt) < 2:
            raise Exception("Invalid input count. Expected 2, got {0}. {1}".format(len(self.chain_jnt), self.chain_jnt))

        super(Twistbone, self).build(rig, create_grp_anm=False, *args, **kwargs)

        nomenclature_rig = self.get_nomenclature_rig(rig)
        nomenclature_jnt = self.get_nomenclature_jnt(rig)

        jnt_s = self.chain_jnt[0]
        jnt_e = self.chain_jnt[1]

        # Create curve from input joints (we'll use maya splineIKEffector for our upnodes.
        num_steps = 2
        self.ikCurve = libRigging.create_nurbsCurve_from_joints(jnt_s, jnt_e, 2 if num_steps > 2 else 1)
        self.ikCurve.rename(self.name + "_crv")
        pymel.parentConstraint(jnt_s, self.ikCurve, maintainOffset=True)

        # Generate Subjoints if necessary
        if not self.subjnts:
            self.subjnts = libRigging.create_chain_between_objects(jnt_s, jnt_e, 4)

            #TODO : Use the nomeclature system to name the bones
            for i, sub_jnt in enumerate(self.subjnts):
                sub_jnt.segmentScaleCompensate.set(0) #Remove segment scale compensate
                #Right now, we take into consideration that the system will be named Side_SysName(Ex:Upperarm_Twist)
                jnt_name = nomenclature_jnt.resolve("twist{0:02d}".format(i))
                sub_jnt.rename(jnt_name)

        # Create splineIK
        #Do not connect the stretch to prevent scaling problem
        #TODO : If a stretch system exist on the input, we need to find a way to connect it to the twist system
        splineIK = SplineIK(self.subjnts +[self.ikCurve], name=nomenclature_rig.resolve("splineik"))
        splineIK.bStretch = False
        splineIK.build(rig, create_grp_anm=False, stretch=False)
        self.ikCurve.setParent(splineIK.grp_rig)

        nonroll_1 = NonRollJoint()
        nonroll_1.build()
        nonroll_1.rename(nomenclature_rig.resolve('nonroll_s'))
        nonroll_1.start.rename(nomenclature_jnt.resolve("nonroll_s_start"))
        nonroll_1.end.rename(nomenclature_jnt.resolve("nonroll_s_end"))
        jnt_s_parent = jnt_s.getParent()
        nonroll_1.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True)
        if jnt_s_parent:
            pymel.parentConstraint(jnt_s_parent, nonroll_1.node, maintainOffset=True, skipTranslate=['x', 'y', 'z'])
            pymel.pointConstraint(jnt_s, nonroll_1.node)

        pymel.parentConstraint(jnt_s, nonroll_1.ikHandle, maintainOffset=True)

        nonroll_2 = NonRollJoint()
        nonroll_2.build()
        nonroll_2.rename(nomenclature_rig.resolve('nonroll_e'))
        nonroll_2.start.rename(nomenclature_jnt.resolve("nonroll_e_start"))
        nonroll_2.end.rename(nomenclature_jnt.resolve("nonroll_e_end"))
        nonroll_2.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True)
        nonroll_2.setTranslation(jnt_e.getTranslation(space='world'), space='world')
        pymel.parentConstraint(jnt_s, nonroll_2.node, maintainOffset=True, skipTranslate=['x', 'y', 'z'])
        pymel.pointConstraint(jnt_e, nonroll_2.node)
        pymel.parentConstraint(jnt_e, nonroll_2.ikHandle, maintainOffset=True)

        twist_info = pymel.createNode('transform')
        twist_info.rename('twist_info')
        twist_info.setMatrix(nonroll_2.start.getMatrix(worldSpace=True), worldSpace=True)
        twist_info.setParent(nonroll_2.start)
        pymel.aimConstraint(nonroll_2.end, twist_info, worldUpType=2, worldUpObject=jnt_e)

        ref_end = pymel.createNode('transform')
        ref_end.rename('ref_end')
        ref_end.setMatrix(nonroll_2.getMatrix(worldSpace=True), worldSpace=True)
        ref_end.setParent(nonroll_2.node)
        pymel.connectAttr(twist_info.rotate, ref_end.rotate)

        # Create the upnodes
        upnode_s = pymel.createNode('transform', name='upnode_s')
        upnode_s.setMatrix(jnt_s.getMatrix(worldSpace=True))
        upnode_e = pymel.createNode('transform', name='upnode_e')
        upnode_e.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True)
        upnode_e.setTranslation(jnt_e.getTranslation(space='world'), space='world')

        pymel.parentConstraint(nonroll_1.start, upnode_s)
        pymel.parentConstraint(ref_end, upnode_e)

        # Cleanup
        nonroll_1.setParent(self.grp_rig)
        nonroll_2.setParent(self.grp_rig)
        upnode_s.setParent(self.grp_rig)
        upnode_e.setParent(self.grp_rig)
        splineIK.grp_rig.setParent(self.grp_rig)

        # Configure splineIK upnodes parameters
        splineIK.ikHandle.dTwistControlEnable.set(1)
        splineIK.ikHandle.dWorldUpType.set(4) # Object Rotation Up (Start End)
        #TODO : Find a better way to define the foward axis than the side string
        if nomenclature_rig.side == nomenclature_rig.SIDE_R:
            splineIK.ikHandle.dForwardAxis.set(1)
            #splineIK.ikHandle.dWorldUpAxis.set(1)
        pymel.connectAttr(upnode_s.xformMatrix, splineIK.ikHandle.dWorldUpMatrix)
        pymel.connectAttr(upnode_e.xformMatrix, splineIK.ikHandle.dWorldUpMatrixEnd)

        #Compute the Stretch
        attr_stretch_raw = libRigging.create_stretch_node_between_2_bones(jnt_s, jnt_e, self.grp_rig.globalScale)

        #for subjnt in self.subjnts[1:]:
        pymel.connectAttr(attr_stretch_raw, self.subjnts[0].scaleX)

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

        # Unparent the twistbones so they squash correctly, even in a Game-Engine scenario.
        if self.subjnts[0].getParent() != self.chain_jnt.start:
            self.subjnts[0].setParent(self.chain_jnt.start)

        if self.auto_skin:
            skin_deformers = self.get_skinClusters_from_inputs()

            for skin_deformer in skin_deformers:
                # Ensure the source joint is in the skinCluster influences
                influenceObjects = skin_deformer.influenceObjects()
                if self.chain_jnt.start not in influenceObjects:
                    continue

                # Add new joints as influence.
                for subjnt in self.subjnts:
                    if subjnt in influenceObjects:
                        continue

                    skin_deformer.addInfluence(subjnt, lockWeights=True, weight=0.0)
                    subjnt.lockInfluenceWeights.set(False)

            # TODO : Automatically skin the twistbones
            for mesh in self.get_farest_affected_meshes(rig):
                print("{1} --> Assign skin weights on {0}.".format(mesh.name(), self.name))
                libSkinning.transfer_weights_from_segments(mesh, self.chain_jnt.start, self.subjnts)


        '''