Hi all,
TLDR; In Maya 2015-2018,
getAttr
returns stale value when used in conjunction with anikHandle
I’ve ventured into the depths of querying attributes at an arbitrary time..
# Example
from maya import cmds
cmds.getAttr("myNode.translateX", time=5)
And it has worked well so far, except for when IK is involved.
from maya import cmds
# Build Scene
# o o
# \ /
# \ /
# o
#
cmds.file(new=True, force=True)
a = cmds.joint(name="Root")
cmds.move(0, 0, 20)
cmds.joint()
cmds.move(0, 0, 0)
b = cmds.joint()
cmds.move(0, 10, -20)
handle, eff = cmds.ikHandle(
solver="ikRPsolver",
startJoint=a,
endEffector=b,
)
assert cmds.getAttr(a + ".rx") == 0.0
assert cmds.getAttr(a + ".rx", time=1) == 0.0 # Cycle warning!
Suddenly, Maya throws a cycle warning. Why is that?
Furthermore, the value returned isn’t right anymore.
# Move handle
cmds.move(0, 15, -10, "|ikHandle1")
assert round(cmds.getAttr(a + ".rx"), 0) == -14.0
assert cmds.getAttr(a + ".rx", time=1) == 0.0 # Bad value, no warning..
Where I should see -14.0
I get the previous value, 0.0
. Why is that?
And so the story goes, until..
# Move root
cmds.move(0, 0, 21, a)
assert round(cmds.getAttr(a + ".rx"), 0) == -14.0
assert round(cmds.getAttr(a + ".rx", time=1), 0) == -14.0 # Correct
Viola, all values are correct. Why is that?? :O
This happens consistently across all versions of Maya, except 2015 doesn’t throw a Cycle Warning.
Anyone stumbled upon this before?
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmODV_eKp2%3DJ3AdUufuDfBe2qojSWFjNpT9gJO1A6e9OLhg%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Helps to format :)
I call it a bug, but it’s been around a loooong time.
Maya updates the ikHandles in a separate pass (there’s just one solver for multiple handles), and I don’t believe that script mode forces an update on the solver, where moving the root updates the DAG directly.
It’s one of the several places where the maya internals bypass the DAG so that they can work (IK handles don’t put input connections on the joint rotations as that would be a cycle, so the update needs to live in a callback somewhere, which isn’t triggered by your code). Another place where things get weird is with editable animation curves (the ones where you’ve got an editable curve in the scene that updates the animation curves).
Long story short, querying an attribute at a given time only works for direct DAG evaluation. Anything which operates outside of the DAG won’t be updated when you call getAttr
. For those cases, you actually have to set the time and refresh the evaluation prior to the getAttr
call:
Ok, I’ve got a workaround.
time - 1
firstNow the result is always correct, with or without IK, at the expense of one more query.
Example
One quick way of achieving the effect is wrapping any call to getAttr
with this.
def getAttr(attr, time):
cmds.getAttr(attr, time=time - 1)
return cmds.getAttr(attr, time=time)
Full working example
from maya import cmds
# Build Scene
# o o
# \ /
# \ /
# o
#
cmds.file(new=True, force=True
)
a = cmds.joint(name="Root", position=(0, 0, 20))
b = cmds.joint(position=(0, 0, 0), relative=False)
c = cmds.joint(position=(0, 10, -20), relative=False)
handle, eff = cmds.ikHandle(
solver="ikRPsolver",
startJoint=a,
endEffector=c,
)
def getAttr(attr, time):
cmds.getAttr(attr, time=time - 1)
return cmds.getAttr(attr, time=time)
assert cmds.getAttr(a + ".rx") == 0.0
assert cmds.getAttr(a + ".rx", time=1) == 0.0
# Move handle
cmds.move(0, 15, -10, "|ikHandle1")
assert round(cmds.getAttr(a + ".rx"), 0) == -14.0
assert round(getAttr(a + ".rx", time=1), 0) == -14.0
# The above call also enables this other node to evaluate properly
# Which means we only need to call the special function once
# per IK node.
assert round(cmds.getAttr(b + ".rx", time=1), 0) == 49.0
cmds.move(0, 15, -15, "|ikHandle1")
assert round(getAttr(a + ".rx", time=1), 0) == -4.0
This (luckily) works consistently across all current versions of Maya.
It doesn’t quite jive with my understanding of IK and what Joe mentioned, that it uses this internal callback. This workaround shouldn’t work, so I’m a little weary still. Finally, I found that the Cycle Warning only happens on Maya 2018, no other version; not even 2019. So I’m tempted to just ignore it.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOAQpKQaB8FxFKkQpMup5qhu4KnoU5ATHT-_w%2BT1%3DT7TjA%40mail.gmail.com.
I would make that assumption too. There’s one more thing hinting towards it being outside of the overall DAG loop, which is that changes happening in response to IK don’t trigger callbacks, in particular the maya.api.MDagMessage.addWorldMatrixModifiedCallback
.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAM33%3Da6YmeXBz%3DNT_Usr7gpUxKsjNsMXbsuaGrXxM-n0JMtKvQ%40mail.gmail.com.