Footroll Interface
As usual with nodelling + fluff videos there isn’t a lot to be said here.
We establish more boundaries for the footroll work, in the form of the interface details for the smallest possible bit, and we get to work on that.
Enjoy,
As usual with nodelling + fluff videos there isn’t a lot to be said here.
We establish more boundaries for the footroll work, in the form of the interface details for the smallest possible bit, and we get to work on that.
Enjoy,
Not a lot to add here that won’t be obvious from the video itself.
I originally intended to start more from the animation interface, but ended up discussing graphs as a problem solving notion with a fair chunk of actual rigging in Maya first.
We’ll start on an implementation proper and discuss the interface next episode.
Enjoy,
In the interest of building up vocabulary on math fundamentals for the rest of the season we introduce Set and Graph Theory.
Both the video description and a comment under it contain an Errata Corrige I have to make here as well:
While talking about the inverse function of sin, arcsin, I mention the co-domain of the function being -pi to +pi; that is incorrect, as the domain of arcsin spans the half circle, and therefore is -1/2pi to +1/2pi
This wraps up the pilot season of the stream. Thanks everybody for following.
This episode is about an often overlooked feature of Maya, at least in rigging, assets.
Also, courtesy of a viewer there is an answer to my ramblings about proxy attributes that takes place towards the end, and while for now it’s a commands only thing, and it will alter your graph in potentially circular ways, it seems serviceable already.
The flag I show in the API also has a related command flag to establish an attribute as a proxy, and it’s pretty simple to use.
In its mel form:
addAttr -ln "proxyName" -proxy "sourceObject.attr" targetObject
And here’s the video, Enjoy:
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) |