Exemplo n.º 1
0
def create_line(objects, attach=True, attachParents=[], name=""):
    """
    Creates a line between objects, that optionally
    attaches to each
    """
    if not name:
        name = "line_display"

    print("Attach is: {}".format(attach))
    positions = get_positions(objects)
    curve = create_from_points(positions, name=name, degree=1)

    # Rig CVs to each object
    if attach:
        if not attachParents:
            attachParents = objects[:]

        print("Attaching...")
        cvs = get_cvs(curve)

        for i in range(len(objects)):
            cluster = cmds.cluster(cvs[i])
            cmds.parentConstraint(objects[i], cluster, maintainOffset=True)

    return curve
Exemplo n.º 2
0
def get_avg_pos(nodes, asPoints=False):
    """
    Gets average position between objects
    :param str|list nodes:
    :return position:
    """
    # Get positions from nodes
    positions = dag.get_positions(nodes, asPoints=asPoints)

    # Create an MVector to hold our result
    result = om.MVector()

    # Do sum of positions, then divide them by number of positions
    # to get average position, or mid point from origin
    for pos in positions:
        result += pos

    result /= len(positions)

    return result
Exemplo n.º 3
0
def get_pole_vector(A, B, C, factor=2):
    """
    Given three joitns (ex: shoulder, elbow, wrist),
    creates a locator at a convenient position for a poleVector control

    :param jointA: starting joint
    :param jointB: middle joint
    :param jointC: end joint
    """
    # Create a locator
    loc = cmds.spaceLocator(name="poleVector_temp")

    # Get joint positions
    positions = dag.get_positions([A, B, C], space='world')

    # Get their middle position
    middle = get_avg_pos([A, C])

    # Get a vector aiming down the first joint to the third one
    downAxis = positions[2] - positions[0]
    downAxis.normalize()

    # Get a vector aiming from the middle position, to the middle joint
    aimAxis = positions[1] - middle
    distanceToMid = aimAxis.length()
    aimAxis.normalize()

    # Get orthogonal vectors to form a basis that will describe orientation
    upAxis = aimAxis ^ downAxis
    downAxis = aimAxis ^ upAxis

    # Now we can build an orthonormal basis
    matRotation = om.MMatrix()

    matRotation.setElement(0, 0, aimAxis.x)
    matRotation.setElement(0, 1, aimAxis.y)
    matRotation.setElement(0, 2, aimAxis.z)

    matRotation.setElement(1, 0, upAxis.x)
    matRotation.setElement(1, 1, upAxis.y)
    matRotation.setElement(1, 2, upAxis.z)

    matRotation.setElement(2, 0, downAxis.x)
    matRotation.setElement(2, 1, downAxis.y)
    matRotation.setElement(2, 2, downAxis.z)

    # Get a rotation from it
    rotation = om.MTransformationMatrix(matRotation).rotation()

    # Get a matrix describing the average position between joitns
    mat = om.MTransformationMatrix(om.MMatrix())
    mat.translateBy(middle, om.MSpace.kWorld)

    # Orient to orthonormal basis
    mat.rotateBy(rotation, om.MSpace.kWorld)

    # Move by twice the distance from mid point to mid joint, in object space
    mat.translateBy(om.MVector(distanceToMid * factor, 0.0, 0.0),
                    om.MSpace.kObject)

    # Finally, apply matrix to locator
    cmds.xform(loc, matrix=mat.asMatrix(), worldSpace=True)
Exemplo n.º 4
0
def add_no_roll_joints(start_jnt,
                       end_jnt,
                       n=3,
                       source_axis="X",
                       target_axis="X"):
    """
    Add n number of roll joints between start and end joints, that negate
    some portion of the twist of start_jnt, and are parented to start_jnt

    .. note::
        This solution will flip when driver goes past 180,
        for a non-flip, use custom nodes or weighted orients with IKs

    :param str|MayaBaseNode start_jnt:
    :param str|MayaBaseNode end_jnt:
    :param int n: number of joints to create
    :returns tuple(roll_joints, new_nodes): newly created joints and nodes
    """
    roll_joints = list()
    new_nodes = list()

    assert n >= 3, "You need at least 3 joints"

    if not isinstance(start_jnt, MayaBaseNode):
        start_jnt = MayaBaseNode(start_jnt)

    if not isinstance(end_jnt, MayaBaseNode):
        end_jnt = MayaBaseNode(end_jnt)

    # Calculate step, we want a joint at the beginning and end
    step = 1.0 / (n - 1)

    # Get a vector from start to end
    a, b = dag.get_positions([start_jnt.long_name, end_jnt.long_name])

    target_vector = b - a
    side = start_jnt.side
    descriptor = start_jnt.descriptor

    # Create twist extractor
    local_mtx = cmds.createNode('multMatrix')
    cmds.connectAttr(start_jnt.plug('worldMatrix[0]'),
                     local_mtx + '.matrixIn[0]')

    # Hold inverse matrix of start joint
    hold_mtx = cmds.createNode('holdMatrix')
    cmds.connectAttr(start_jnt.plug('worldInverseMatrix[0]'),
                     hold_mtx + '.inMatrix')
    cmds.connectAttr(hold_mtx + '.outMatrix', local_mtx + '.matrixIn[1]')

    # Disconnect input to cache value
    cmds.disconnectAttr(start_jnt.plug('worldInverseMatrix[0]'),
                        hold_mtx + '.inMatrix')

    # Decompose result matrix
    twist_dcm = cmds.createNode('decomposeMatrix')
    cmds.connectAttr(local_mtx + '.matrixSum', twist_dcm + '.inputMatrix')

    # Output quaternion to an inverse quat to 'negate' the parent's twist about axis
    quat_inv = cmds.createNode('quatInvert')
    cmds.connectAttr(twist_dcm + '.outputQuat', quat_inv + '.inputQuat')

    # Connect the inverse to a quatToEuler so we can connect it to joint's rotation
    quat_to_euler = cmds.createNode('quatToEuler')
    cmds.setAttr(quat_to_euler + '.inputQuatW', 1)
    cmds.connectAttr(quat_inv + '.outputQuat{}'.format(source_axis),
                     quat_to_euler + '.inputQuat{}'.format(source_axis))

    new_nodes.extend([local_mtx, hold_mtx, twist_dcm, quat_inv, quat_to_euler])
    source_axis = source_axis.upper()
    target_axis = target_axis.upper()

    for i in range(n):
        num = str(i + 1).zfill(2)

        # Create a roll extraction joint
        jnt_name = compose(node_type="joint",
                           role="roll",
                           descriptor=descriptor + num,
                           side=side)
        new_jnt = MayaBaseNode(cmds.joint(name=jnt_name))
        new_jnt.snap_to(start_jnt.long_name, rotate=True, translate=True)
        new_jnt.set_position(a + (target_vector * (step * i)))
        new_jnt.parent = start_jnt.long_name

        # Freeze rotations
        cmds.makeIdentity(new_jnt.long_name, r=True, apply=True)
        roll_joints.append(new_jnt)

        # Multiply result by a factor, so we don't twist all the way
        # this creates some conversion nodes, but quaternion slerp crashes maya
        mdl = cmds.createNode("multDoubleLinear")
        cmds.connectAttr(quat_to_euler + '.outputRotate{}'.format(source_axis),
                         mdl + '.input1')
        cmds.setAttr(mdl + '.input2', 1 - (step * i))

        # Connect result
        cmds.connectAttr(mdl + '.output',
                         new_jnt.long_name + '.rotate{}'.format(target_axis))

        # Store nodes
        new_nodes.append(mdl)

    return roll_joints, new_nodes
Exemplo n.º 5
0
def add_roll_joints(start_jnt, end_jnt, n=2, source_axis="X", target_axis="X"):
    """
    Add n number of roll joints between start and end joints, that gain some
    proportion of the twist of the end_joint, and are parented to start_jnt

    .. note::
        This solution will flip when driver goes past 180,
        for a non-flip, use custom nodes or weighted orients with IKs

    :param str|MayaBaseNode start_jnt:
    :param str|MayaBaseNode end_jnt:
    :param int n:
    :returns list roll_joints: newly created joints
    """
    roll_joints = list()
    new_nodes = list()

    if not isinstance(start_jnt, MayaBaseNode):
        start_jnt = MayaBaseNode(start_jnt)

    if not isinstance(end_jnt, MayaBaseNode):
        end_jnt = MayaBaseNode(end_jnt)

    # Calculate step
    step = 1.0 / n

    # Get a vector from start to end
    a, b = dag.get_positions([start_jnt.long_name, end_jnt.long_name])

    target_vector = b - a
    side = start_jnt.side
    descriptor = start_jnt.descriptor

    # Create twist extractor
    # First, create a multMatrix node to set our target in local space
    local_mtx = cmds.createNode('multMatrix')
    cmds.connectAttr(end_jnt.plug('worldMatrix[0]'),
                     local_mtx + '.matrixIn[0]')
    cmds.connectAttr(start_jnt.plug('worldInverseMatrix[0]'),
                     local_mtx + '.matrixIn[1]')

    # Decompose result
    local_dcm = cmds.createNode('decomposeMatrix')
    cmds.connectAttr(local_mtx + '.matrixSum', local_dcm + '.inputMatrix')

    # Convert it to degrees
    quat_to_euler = cmds.createNode('quatToEuler')
    cmds.setAttr(quat_to_euler + '.inputQuatW', 1)
    cmds.connectAttr(local_dcm + '.outputQuat{}'.format(source_axis),
                     quat_to_euler + '.inputQuat{}'.format(source_axis))

    new_nodes.extend([local_mtx, local_dcm, quat_to_euler])

    source_axis = source_axis.upper()
    target_axis = target_axis.upper()

    for i in range(1, n + 1):
        # Compose a name for the new joint
        num = str(i).zfill(2)
        jnt_name = compose(node_type='joint',
                           role='roll',
                           descriptor=descriptor + num,
                           side=side)

        # Create roll joint and snap to location
        new_jnt = MayaBaseNode(cmds.joint(name=jnt_name))
        new_jnt.snap_to(start_jnt.long_name)
        new_jnt.set_position(a + (target_vector * (step * i)))

        # Freeze joint rotations
        cmds.makeIdentity(new_jnt, r=True, apply=True)
        cmds.setAttr(new_jnt.plug('displayLocalAxis'), 1)

        # Parent under start_jnt and append to result
        new_jnt.parent = start_jnt.long_name
        roll_joints.append(new_jnt.long_name)

        # Multiply result by a factor, so we don't twist all the way
        # this creates some conversion nodes, but quaternion slerp crashes maya
        mdl = cmds.createNode("multDoubleLinear")
        cmds.connectAttr(quat_to_euler + '.outputRotate{}'.format(source_axis),
                         mdl + '.input1')
        cmds.setAttr(mdl + '.input2', step * i)

        # Connect result
        cmds.connectAttr(mdl + '.output',
                         new_jnt.long_name + '.rotate{}'.format(target_axis))

        # Store nodes
        new_nodes.extend([quat_to_euler])

    return roll_joints, new_nodes