Skip to content

Pilot Season – Day 16 – Automatically loading callbacks on scene load

Day 16 of the stream, this is where we wrap callbacks for good and show how to automatically deploy one on the rig at scene load time through a Script Node

Closing the callback saga we finally wrap it all together, make the callback work with the UI when an attribute is slid and not just changed, and finally make sure it auto deploys on the rig whenever the scene is opened through the use of a Script Node.

As usual with scripting heavy sessions at the end of this post you will find the transcriptions of the final result, in this case the script to add to the Script Node that will deploy the callback on scene load events.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
from maya.api import OpenMaya as om2
from maya import cmds
import math
 
 
def removeCallbacksFromNode(node_mob):
    """
    :param node_mob: [MObject] the node to remove all node callbacks from
    :return: [int] number of callbacks removed
    """
    cbs = om2.MMessage.nodeCallbacks(node_mob)
    cbCount = len(cbs)
    for eachCB in cbs:
        om2.MMessage.removeCallback(eachCB)
    return cbCount
 
 
def cb(msg, plug1, plug2, payload):
    fkik_attrName = 'FKIK_switch'
    if msg != 2056:  # check most common case first and return unless it's
        return  # an attribute edit type of callback
 
    if not plug1.partialName(includeNodeName=False, useAlias=False) == fkik_attrName:
        # We ensure if the attribute being changed is uninteresting we do nothing
        return
 
    isFK = plug1.asBool() == False  # Switched To FK
    isIK = not isFK  # Switched to IK
 
    settingsAttrs = {  # all interesting attribute names in keys, respective plugs in values
        'fkRotation': None,
        'ikRotation': None,
        'fk_ctrl_rotx': None,
        'ik_ctrl_translate': None,
        'ikPedalOffset': None,
        'dirtyTracker': None,
    }
 
    mfn_dep = om2.MFnDependencyNode(plug1.node())
    # We populate the dictionary of interesting attributes with their plugs
    for eachPName in settingsAttrs.iterkeys():
        plug = mfn_dep.findPlug(eachPName, False)
        settingsAttrs[eachPName] = plug
 
    for p in settingsAttrs.itervalues():
        # We will exit early and do nothing if a plug couldn't be initialised, the object
        #  is malformed, or we installed the callback on an object that is only
        #  conformant by accident and can't operate as we expect it to.
        if p is None:
            return
 
    dirtyTrackerPlug = settingsAttrs.get('dirtyTracker')
    isDirty = dirtyTrackerPlug.asBool() != plug1.asBool()
    if isDirty:
        dirtyTrackerPlug.setBool(plug1.asBool())
    else:
        return
 
    angle = None  # empty init
    if isFK:
        # Simplest case, if we switched to FK we copy the roation from IK
        #  to the FK control's X rotation value
        angle = -settingsAttrs.get("ikRotation").source().asDouble()
        fkSourcePlug = settingsAttrs.get("fk_ctrl_rotx").source()
        fkSourcePlug.setDouble(angle)
    elif isIK:
        # If instead we switched to IK we need to
        #  derive the translation of the IK control that produces the result
        #  of an equivalent rotation to the one coming from the FK control
        angle = settingsAttrs.get("fkRotation").source().asDouble()
        projectedLen = settingsAttrs.get("ikPedalOffset").source().asDouble()
 
        y = (math.cos(angle) * projectedLen) - projectedLen
        z = math.sin(angle) * projectedLen
 
        ikSourcePlug = settingsAttrs.get("ik_ctrl_translate").source()
        for i in xrange(ikSourcePlug.numChildren()):
            realName = ikSourcePlug.child(i).partialName(includeNodeName=False, useAlias=False)
            if realName == 'ty':
                ikSourcePlug.child(i).setDouble(y)
            elif realName == 'tz':
                ikSourcePlug.child(i).setDouble(z)
 
 
 
# full scene DAG path to the object we're looking for, our settings panel
settingsStrPath = "unicycle|pedals_M_cmpnt|control|pedals_M_settings_ctrl"
# check for its existence
found = cmds.objExists(settingsStrPath)
 
if found: # act only if found
    # the following is A way to get an MObject from a name in OM2
    sel = om2.MSelectionList();
    sel.add(settingsStrPath)
    settingsMob = sel.getDependNode(0)
 
    # first we need to ensure the node isn't dirty by default
    #  by aligning the value of the switch the rig was save with
    #  into the dirty tracker
    mfn_dep = om2.MFnDependencyNode(settingsMob)
    fkikPlug = mfn_dep.findPlug('FKIK_switch', False)
    dirtyTracker = mfn_dep.findPlug('dirtyTracker', False)
    dirtyTracker.setBool(fkikPlug.asBool())
 
    # as this is on scene open only the following callback removal
    #  shouldn't be necessary since callbacks don't persist on scene save,
    #  but why not?
    removeCallbacksFromNode(settingsMob)
 
    # and we finally add the callback implementation to the settings node
    om2.MNodeMessage.addAttributeChangedCallback(settingsMob, cb)
Published inStreamVideo Archives