Home Website Youtube GitHub

About Pole vector space switches

Hi, once more.
I’ve noticed that if you make a leg (I’ve used leg_2jnt_freeTangents_01), and you leave blank the UpV Reference Array list, you get a nice legUpVRef attribute all the same. So it’s like a handy “default” space switcher where you can switch from auto (global, more or less) to ik_foot.
Now, the same thing doesn’t happen with the arms (using arm_ms_2jnt_01 modules). You don’t get a “default” spaceSwitcher.
It shouldn’t be a big deal, though: now is when I set the UpV Reference Array. But if, for instance I use the arm_L0_wrist from the shoulder module (a shoulder_01) and the arm_L0_wrist from the same arm module, when I build, I get the spaceSwitcher, all right, but when I switch to the wrist, it gives me a cycle warning. And I really, really hate cycles. I try to avoid them as much as possible. They always mean problems. And that’s because your automated space switch system seems to exclusively rely on joints. Whereas I would use ctrls instead.

My question, then is: is there a way to set a ctrl as a base for the spaceswitch? Or is there a way to set the ik elbow PV have a “default” space switch (towards world and hand), as the knee has?

Thanks and cheers

More info on this. I think I just found a bug. As I said, “leg_2jnt_freeTangents_01” leg modules automatically make a default UpV even if nothing is set in the legUpVRef text field.
But what happens when you try to add more nodes to this text field? That the default nodes are kept as spaceSwitching targets and the nodes you choose, are added on top. With one very big drawback. The system creates condition nodes to connect them. The default condition nodes are ok and they work. But the custom ones are created with a wrong value in the “second term” attibute.
If, for example, I added two more nodes to the space switch:
defaultTarget#1’s condition second term value is 0,
defaultTarget#2 is 1,
customTarget#3 gets a value of 3 and
customTarget#4 gets a value of 4!
Thus, when we try to switch to customTarget#3 all constraint targets get a value of 0.
And when we try to switch on customTarget#4, we get customTarget#3 instead. Target#4 cannot be turned on.
The solution is rather simple, but I had to tell you about it.

1 Like

I can second this. I’ve seen this index bug.

I am facing the bug on my side also.
If it can help, here is a post script that I use to fix UpV_Ref on components:
In my case the components needing fixing are called “hindleg”, “foreleg” and “forelegAssist” but just rename those in the script to match your components :wink:

import mgear.shifter.custom_step as cstp
import pymel.core as pm

class CustomShifterStep(cstp.customShifterMainStep):
    def __init__(self):
        self.name = "fix_upvref_001"

    def run(self, stepDict):
        """Run method.

            i.e:  stepDict["mgearRun"].global_ctl  gets the global_ctl from
                    shifter rig build bas
            i.e:  stepDict["mgearRun"].components["control_C0"].ctl  gets the
                    ctl from shifter component called control_C0
            i.e:  stepDict["otherCustomStepName"].ctlMesh  gets the ctlMesh
                    from a previous custom step called "otherCustomStepName"
        Arguments:
            stepDict (dict): Dictionary containing the objects from
                the previous steps

        Returns:
            None: None
        """

        for str_component in ["hindleg", "foreleg", "forelegAssist"]:
            for str_polarity in ["L", "R"]:
                self.fix_upvect_ref(stepDict["mgearRun"].components[str_component + "_" + str_polarity + "0"])

        return

    @staticmethod
    def fix_upvect_ref(cmpnt):
        upv_parent_cns = pm.listConnections(cmpnt.upv_cns, destination=False, source=True, type='parentConstraint')[0]
        for node_condition in pm.listConnections(upv_parent_cns, destination=False, source=True, type='condition'):
            second_term = node_condition.secondTerm.get()
            if second_term == 1:
                node_condition.secondTerm.set(-1)
            elif second_term > 1:
                node_condition.secondTerm.set(second_term - 1)
2 Likes

Thanks, CarlosV. I’ll take a look. Although I think it’s easier to just hardcode the condition nodes to set the number I need.

Ok. Yes, now I see it’s pretty much what your code does…

Hi. I’ve just learnt something new. In fact, there is a fifth condition node connected with output of the “leg_upvref” attribute. That condition (that I guess it’s created by default) is not connected to anything. But that’s the condition that gets a “2” value in its second term…

Wrong. The other condition node is indeed connected, but it doesn’t have a value in the “leg_upvref” attribute.
It seems that the “default UpV” gets in the middle when trying to do a “custom upV”.
If you don’t enter any UpV node in the settings window, what you get nonetheless is a parentConstraint with three (i thought there were two, but no: they where three) values:
legL0LegUpvRef0JntW0
legL0IkCtlW1
worldCtlW2
And each one has a condition with its second term properly set as 0, 1 and 2.
The thing is, the attribute, by default only lets you choose between two values: auto and foot. So, you can never set your knee to follow the world node.
If you do a custom UpV, This problem remains. You’ll get auto, foot, and any node you want the poleVector ctrl to be attached to, but the world will still be there, occupying one slot that never gets to the attribute. That’s the reason for the problem.
Maybe if the default UpV system included a world value in the attribute, none of these would happen.

Hi. This can do the trick. I hope it helps.

import maya.cmds as cmds

for side in 'LR':
    # If your host doesn't match this one, you should change the following line:
    hostNode = 'legUI_{}0_ctl'.format(side)
    getEnumValues = cmds.addAttr ("{}.leg_upvref".format(hostNode), q=True, enumName=True)
    newEnumValues = getEnumValues.replace ('Auto:ikFoot:','Auto:ikFoot:World:')
    cmds.addAttr ("{}.leg_upvref".format(hostNode), e=True,enumName=newEnumValues)
2 Likes

This seems to be an issue with this backwards compatibility in the connectRef function in component/init.py. If you remove the second condition and only get_valid_ref_list then the constraint order is correct.

From this:
def connectRef(self, refArray, cns_obj, upVAttr=None, init_refNames=False):
“”"Connect the cns_obj to a multiple object using parentConstraint.

    Args:
        refArray (list of dagNode): List of driver objects
        cns_obj (dagNode): The driven object.
        upVAttr (bool): Set if the ref Array is for IK or Up vector
    """
    if refArray:
        if upVAttr and not init_refNames:
            # we only can perform name validation if the init_refnames are
            # provided in a separated list. This check ensures backwards
            # copatibility
            ref_names = refArray.split(",")
        else:
            ref_names = self.get_valid_ref_list(refArray.split(","))

To this:
def connectRef(self, refArray, cns_obj, upVAttr=None, init_refNames=False):
“”"Connect the cns_obj to a multiple object using parentConstraint.

    Args:
        refArray (list of dagNode): List of driver objects
        cns_obj (dagNode): The driven object.
        upVAttr (bool): Set if the ref Array is for IK or Up vector
    """
    if refArray:
        ref_names = self.get_valid_ref_list(refArray.split(","))

Hope that helps find the actual issue

2 Likes